Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give compiler hints when handling run-time deps in parse_transforms #2606

Merged
merged 3 commits into from
Aug 24, 2021

Conversation

ferd
Copy link
Collaborator

@ferd ferd commented Aug 16, 2021

Assume that the original app order respects the dependency order as supplied by
the rebar3 dependency order. Then what we do is interleave the hard compile-
time dependencies as found by analysis to maintain their ordering constraints
into those given by the dependency order.

This should lower the chance of hitting conflicts when runtime dependencies
become compile-time dependencies via interactions in parse transform calls.

As described in #2563,
part of the previous approach's weakness is that we need to
seed the compile order with the dependency order for some
invisible compile-time dependencies (runtime ones turned
to build-time ones via calls to parse-transforms).

Unfortunately, topological sorting is insufficient due to how it
flattens apps. Assume the DAG contains:

a -> b
c -> d

then a valid topological sort would be [a,c,b,d]. However, if we
introduce the hidden dep (and decide that c calls bin a
parse_transform) that the DAG doesn't contain, then the sorting order
is broken.

However, if we initially carried the [x,b,a,c,f,e,d] dep list,
we can use a run over it to build [x,a,b,c,f,e,d] final compile
order.

This is unlikely to show up for project apps, where no such information
exists, but a similar hint can be given through include files under
the developer's control.

ferd added 2 commits July 31, 2021 12:26
Assume that the original app order respects the dependency order as supplied by
the rebar3 dependency order. Then what we do is interleave the hard compile-
time dependencies as found by analysis to maintain their ordering constraints
into those given by the dependency order.

This should lower the chance of hitting conflicts when runtime dependencies
become compile-time dependencies via interactions in parse transform calls.
As described in erlang#2563,
part of the previous approach's weakness is that we need to
seed the compile order with the dependency order for some
invisible compile-time dependencies (runtime ones turned
to build-time ones via calls to parse-transforms).

Unfortunately, topological sorting is insufficient due to how it
flattens apps. Assume the DAG contains:

    a -> b
    c -> d

then a valid topological sort would be `[a,c,b,d]`. However, if we
introduce the hidden dep (and decide that `c` calls `b`in a
parse_transform) that the DAG doesn't contain, then the sorting order
is broken.

However, if we initially carried the `[x,b,a,c,f,e,d]` dep list,
we can use a run over it to build `[x,a,b,c,f,e,d]` final compile
order.
@ferd
Copy link
Collaborator Author

ferd commented Aug 16, 2021

I'm not quite sure how to set up a test for this, but may add a small unit one.

@ferd ferd requested a review from tsloughter August 20, 2021 13:47
@tsloughter
Copy link
Collaborator

For tests, @tothlac has an example project, right? Could we add that whole thing to rebar3_tests?

@ferd
Copy link
Collaborator Author

ferd commented Aug 21, 2021

It has some private content that required sharing it privately but I can see if I can extract a minimal case

ferd added a commit to tsloughter/rebar3_tests that referenced this pull request Aug 21, 2021
And fixed in erlang/rebar3#2606

Relies on having all relevant libs compiled in the same phase (i.e. all
top-level apps or all deps), with two layers of transitive libraries
depending on parse_trans, which is called at runtime during the
compiler phase. If the compiler does not feed dep-level information
and does no runtime analysis of compile time deps, either a_lib or z_lib
will fail to build when b_lib calls parse_trans (depending on sorting order).
@ferd
Copy link
Collaborator Author

ferd commented Aug 21, 2021

@tsloughter added in tsloughter/rebar3_tests#8

[];
interleave([App|Apps], DAG, Expanded) ->
case Expanded of
#{App := _} ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you be using the guard is_map_key in interleave and dedupe instead?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, why is a map used, is Expanded not just acting as a set?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's acting as a set, but the map is generally faster than the set. I can swap the datastructure if you want though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok, I'm fine either way. I did recall discussion about switching some of these data structures internally to use maps and that lead me to seeing sets has a "version 2", https://erlang.org/doc/man/sets.html#new-1

Since we aren't doing any set operations besides is_element I don't think it is too important to switch, but seems we can in the future and possibly not lose speed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm good going for the semantics and using the sets module then. Will update later today.

@ferd ferd merged commit 3169392 into erlang:master Aug 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants