From 89f6262cf03834d8265326b668f9cfed197b04bd Mon Sep 17 00:00:00 2001 From: Timofey Barmin Date: Tue, 4 Nov 2025 16:05:45 -0800 Subject: [PATCH 1/2] Fix re-compilation issue in case of nested projects Problem: Say we have the followign project structure: /root_project rebar.config /apps /app1 /rebar.config <- uses pc and app1 needs to build a NIF library. First trigger of `rebar3 compile` from /root_project builds NIF library correctly. When `rebar3 compile` is triggered the second time, the NIF library will not be re-compiled even if its source files change, because the paths to the NIF sources and NIF objects are relative to /app1, while actual cwd is /root_project in this case. The same error leads to incorrect behavior when cleaning the project (nothing gets cleaned up). Solution: Append project root to source and object paths before checking their last modification times. --- src/pc_compilation.erl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/pc_compilation.erl b/src/pc_compilation.erl index 5d9caac..f559434 100644 --- a/src/pc_compilation.erl +++ b/src/pc_compilation.erl @@ -40,6 +40,7 @@ compile_and_link(State, Specs) -> %% Compile each of the sources NewBins = compile_sources(State, Specs), + Root = rebar_state:dir(State), %% Make sure that the target directories exist lists:foreach(fun(Spec) -> @@ -56,7 +57,8 @@ compile_and_link(State, Specs) -> AllBins = [sets:from_list(Bins), sets:from_list(NewBins)], Intersection = sets:intersection(AllBins), - case needs_link(Target, sets:to_list(Intersection)) of + BinFullPaths = [filename:join(Root, D) || D <- sets:to_list(Intersection)], + case needs_link(Target, BinFullPaths) of true -> LinkLang = pc_port_specs:link_lang(Spec), LinkTemplate = select_link_template(LinkLang, Target), @@ -71,13 +73,15 @@ compile_and_link(State, Specs) -> end end, Specs). -clean(_State, Specs) -> +clean(State, Specs) -> + Root = rebar_state:dir(State), lists:foreach(fun(Spec) -> Target = pc_port_specs:target(Spec), Objects = pc_port_specs:objects(Spec), + ObjectsFullPaths = [filename:join(Root, O) || O <- Objects], rebar_file_utils:delete_each([Target]), - rebar_file_utils:delete_each(Objects), - rebar_file_utils:delete_each(port_deps(Objects)) + rebar_file_utils:delete_each(ObjectsFullPaths), + rebar_file_utils:delete_each(port_deps(ObjectsFullPaths)) end, Specs). %%%=================================================================== @@ -125,7 +129,7 @@ compile_each(State, [Source | Rest], Type, Env, {NewBins, CDB}) -> Cmd = expand_command(Template, Env, Source, Bin), CDBEnt = cdb_entry(State, Source, Cmd, Rest), NewCDB = [CDBEnt | CDB], - case needs_compile(Source, Bin) of + case needs_compile(Source, Bin, rebar_state:dir(State)) of true -> ShOpts = [ {env, Env} , return_on_error @@ -214,8 +218,12 @@ select_compile_drv_template("$CXX") -> "DRV_CXX_TEMPLATE". select_compile_exe_template("$CC") -> "EXE_CC_TEMPLATE"; select_compile_exe_template("$CXX") -> "EXE_CXX_TEMPLATE". -needs_compile(Source, Bin) -> - needs_link(Bin, [Source|bin_deps(Bin)]). +needs_compile(Source, Bin, Root) -> + FullSource = filename:join(Root, Source), + FullBin = filename:join(Root, Bin), + Deps = bin_deps(FullBin), + DepsFullPaths = [filename:join(Root, D) || D <- Deps], + needs_link(FullBin, [FullSource|DepsFullPaths]). %% NOTE: This relies on -MMD being passed to the compiler and returns an %% empty list if the .d file is not available. This means header deps are @@ -224,14 +232,14 @@ bin_deps(Bin) -> [DepFile] = port_deps([Bin]), case file:read_file(DepFile) of {ok, Deps} -> - parse_bin_deps(list_to_binary(Bin), Deps); + parse_bin_deps(Deps); {error, _Err} -> [] end. -parse_bin_deps(Bin, Deps) -> +parse_bin_deps(Deps) -> Ds = re:split(Deps, "\\s*\\\\\\R\\s*|\\s+", [{return, binary}]), - [D || D <- Ds, D =/= <<>>, D =/= <>]. + [D || D <- Ds, D =/= <<>> andalso binary:last(D) =/= $:]. %% %% == linking == From 0169d2d3527f304778f5f2a0898dc45baa7d5376 Mon Sep 17 00:00:00 2001 From: Timofey Barmin Date: Tue, 11 Nov 2025 13:48:42 -0800 Subject: [PATCH 2/2] Add debug logging --- src/pc_compilation.erl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/pc_compilation.erl b/src/pc_compilation.erl index f559434..aa02bb2 100644 --- a/src/pc_compilation.erl +++ b/src/pc_compilation.erl @@ -67,8 +67,15 @@ compile_and_link(State, Specs) -> pc_util:strjoin(Bins, " "), Target), rebar_api:info("Linking ~ts", [Target]), - rebar_utils:sh(Cmd, [{env, Env}, {cd, rebar_state:dir(State)}]); + CD = rebar_state:dir(State), + rebar_api:debug("Link command: ~s~n" + "Current dir: ~p~n" + "Env: ~p", [Cmd, CD, Env]), + {ok, Output} = rebar_utils:sh(Cmd, [{env, Env}, {cd, CD}]), + rebar_api:debug("Linker output: ~ts", [Output]), + ok; false -> + rebar_api:debug("Skip linking ~p, up to date", [Target]), ok end end, Specs). @@ -79,9 +86,13 @@ clean(State, Specs) -> Target = pc_port_specs:target(Spec), Objects = pc_port_specs:objects(Spec), ObjectsFullPaths = [filename:join(Root, O) || O <- Objects], + PortDeps = port_deps(ObjectsFullPaths), + rebar_api:debug("Deleting target: ~p", [Target]), rebar_file_utils:delete_each([Target]), + rebar_api:debug("Deleting obj files: ~p", [ObjectsFullPaths]), rebar_file_utils:delete_each(ObjectsFullPaths), - rebar_file_utils:delete_each(port_deps(ObjectsFullPaths)) + rebar_api:debug("Deleting port deps: ~p", [PortDeps]), + rebar_file_utils:delete_each(PortDeps) end, Specs). %%%=================================================================== @@ -140,6 +151,7 @@ compile_each(State, [Source | Rest], Type, Env, {NewBins, CDB}) -> compile_each(State, Rest, Type, Env, {[Bin | NewBins], NewCDB}); false -> + rebar_api:debug("Skip compilation ~p, up to date", [Bin]), compile_each(State, Rest, Type, Env, {NewBins, NewCDB}) end. @@ -194,17 +206,23 @@ expand_command(TmplName, Env, InFiles, OutFile) -> rebar_api:expand_env_variable(Cmd1, "PORT_OUT_FILE", OutFile1). exec_compiler(_Config, Source, Cmd, ShOpts) -> + rebar_api:info("Compiling ~ts", [Source]), + rebar_api:debug("Compile command: ~s~n" + "Current dir: ~p~n" + "Env: ~p", + [Cmd, proplists:get_value(cd, ShOpts), + proplists:get_value(env, ShOpts)]), case rebar_utils:sh(Cmd, ShOpts) of - {error, {_RC, RawError}} -> + {error, {RC, RawError}} -> AbsSource = filename:absname(Source), - rebar_api:info("Compiling ~ts", [AbsSource]), Error = re:replace(RawError, Source, AbsSource, [{return, list}, global, unicode]), - rebar_api:error("~ts", [Error]), + rebar_api:error("Compiler returned: ~p~n" + "Output: ~ts~n" + "Raw output: ~s", [RC, Error, RawError]), rebar_api:abort(); {ok, Output} -> - rebar_api:info("Compiling ~ts", [Source]), - rebar_api:debug("~ts", [Output]) + rebar_api:debug("Compiler output: ~ts", [Output]) end. select_compile_template(drv, Compiler) ->