rebar3 icon indicating copy to clipboard operation
rebar3 copied to clipboard

Main application's vsn is not stored in rebar's state

Open tothlac opened this issue 2 years ago • 6 comments

Using the following code I'm able to obtain the vsn of the application compilation is currently running on:

get_vsn(State) ->
  CurrentApp =
            case rebar_state:current_app(State) of
                    undefined ->
                      case rebar_state:project_apps(State) of
                        [Info] ->
                          Info;
                        _ ->
                          undefined
                      end;
                    Info ->
                      Info
                  end,
  AppName = rebar_app_info:name(AppInfo),
  AllApps = rebar_state:all_deps(State) ++ rebar_state:project_apps(State),
  AppInfo = lists:keyfind(AppName, 2, AllApps),
  Vsn0 = rebar_app_info:original_vsn(AppInfo),
  rebar_utils:vcs_vsn(AppInfo, Vsn0, State),

Unfortunately, when rebar3 compiles one of the dependencies this will return the vsn of the dependency being compiled. I've printed out the entire rebar state as well, but it looks like the vsn of the main application is not stored. Is there a trick to obtain that somehow? If I can't get it using current rebar3 would it be difficult to add a new field to rebar_state that stores this information?

tothlac avatar May 20 '22 11:05 tothlac

When is this happening? The version of the application is created and applied as part of the compilation steps, but after all the modules have been compiled already:

https://github.com/erlang/rebar3/blob/d5d2915bf34b9700ea7c44b3ecaeb0654f81e606/src/rebar_prv_compile.erl#L156-L158 --> https://github.com/erlang/rebar3/blob/d5d2915bf34b9700ea7c44b3ecaeb0654f81e606/src/rebar_prv_compile.erl#L249-L258 --> https://github.com/erlang/rebar3/blob/d5d2915bf34b9700ea7c44b3ecaeb0654f81e606/src/rebar_otp_app.erl#L39-L56 --> https://github.com/erlang/rebar3/blob/d5d2915bf34b9700ea7c44b3ecaeb0654f81e606/src/rebar_otp_app.erl#L124-L144

This last snippet reads and assigns the version in the appinfo tuple. The version requires the app file, and the app file requires the modules, which means that if you're at any point before modules or the appfile are handled, the version is not yet available.

ferd avatar May 20 '22 12:05 ferd

The get_vsn(State) -> function mentioned above is declared and used by a plugin. When compile is running on the top-level app, it returns the vsn of the top application, but before that the plugin is called on one of the dependencies. When the dependency is being compiled get_vsn/1 returns the vsn of the dependency application, not the vsn of top-level app. The problem is that the plugin would like to use the top-level application's vsn instead of the version of the dependency. Let's say the vsn of the top app is 5.0.0, and the dependency is 4.0.0. As I saw when debugging, the "5.0.0" was not stored in rebar state when the dependency was compiled, but it was in the state during compilation of the top-level application.

tothlac avatar May 23 '22 12:05 tothlac

Dependencies are always compiled before the top-level application, so I don't know if it's even possible to have the top-level app's version accessible when building dependencies.

ferd avatar May 23 '22 13:05 ferd

So basically the only thing I can do is to construct the version string of the top-level application manually, using a similar code that is executed when rebar3 compiles the top-level application?

tothlac avatar May 23 '22 13:05 tothlac

The problem is that I generate some logging modules. There are dep1_logs.erl, ....., depN_logs.erl, top_level_app_logs.erl files. I generate them from csv files. When dep1_logs:log.... function is called from dep1.erl I want to log the top_level application's vsn, and a good place for storing the vsn is a generated -define(TOP_LEVEL_VSN, "....") written somewhere into dep1_logs.erl. The problem is that dep1_logs.erl can be generated only when dep1 application is being compiled. Of course the plugin can generate it during compilation of the top-level app as well, but dep1_logs.beam is compiled only when dep1 is compiled, so I have to generate it before that.

tothlac avatar May 23 '22 13:05 tothlac

Currently, the versions are generated dep-to-app under a more general model where the dependencies are generally required for apps to be built (for example: header files must be present). Under that model, it's more likely for dependencies to be needed by top-level applications than the opposite. They are, after all, dependencies for a reason.

You could currently define an app's version to be something like {cmd, "md5sum rebar.lock | cut -f1 -d' '"}, which would checksum the lock file and decide this is the app version. This file however needs to have been computed prior to the command execution.

So given we support this sort of behavior today, also supporting the opposite (where an app's version is evaluated before the dependencies are in place) results in an actual contradiction. We can't do both at once safely.

ferd avatar May 23 '22 15:05 ferd