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

add plus one deps transitive mode for compilation #698

Merged
merged 7 commits into from
Feb 25, 2019

Conversation

ittaiz
Copy link
Member

@ittaiz ittaiz commented Feb 21, 2019

Tries to balance between sternness of strict-deps off and strict-deps on cache issues and false positives.
We, Wix, have been using strict-deps=error for a long time and have been enjoying the relative ease a developer has when needing to add a new dependency.
However the current strict-deps compiler plugin has a serious drawback in generating too many false positives (causing errors about dependencies which aren't really used because of the compiler eagerness). This is a much bigger issue for us then the cache hits (which we also care about) since this means that:

  1. Builds on master fail from seemingly innocent changes. This is because we have many repositories in a logical head dependency.
  2. Additionally this causes developer fatigue which just try to "please the beast".

OTOH using strict-deps off has similar issues of easily breaking builds. For examples you can see the tests I added and also this scala-center discussion.

This PR adds a new heuristic mode which I hope will strike a better balance between the two. It passes to the compiler all direct dependencies and their direct dependencies since very often this is needed by the compiler. This was also confirmed by offline discussions with the google java people.

We also hope in the future to add some kind of post processing action (inside the bazel build) which might work over the byte code or source code to find strict-deps infringements of these plus one deps.
Additionally we have an internal tool in beta mode which goes over the compiler output and via various analyzers (mainly regex based) finds the needed FQN and then looks them up in bazel-out, external binary dependencies as well as a global index we have for our own source code.
This tool is run outside of bazel by the user.
We hope to open source this tool as well as the global index server API.

tries to balance between sterness of strict-deps off and strict-deps on cache issues and false positives
@ittaiz
Copy link
Member Author

ittaiz commented Feb 21, 2019

btw, I really wanted to refrain from adding the aspect to the deps attribute but rather run the aspect from command line but unfortunately this behaved very differently.

@johnynek
Copy link
Member

@ittaiz can you comment more or share any links about discussions with the google folks? Can you give some references: is this how it works at Google internally, do the standard java rules work this way?

I'd really like to be moving in the other way: less configuration. Adding a new compilation mode adds complexity and tends to cut the effectiveness of our tests since we generally are not multiplicatively expanding our tests to check all configurations.

I wouldn't want to keep inviting new heuristics for people to bolt into the rules that each company thinks is a good solution to the problem. I'd rather push the concerns upstream to scalac where possible, and follow the java standard rules as much as possible.

@ittaiz
Copy link
Member Author

ittaiz commented Feb 21, 2019

the discussions were offline with @cushon.
AFAIU the java rules work similarly to this but not exactly. They add all of the dependencies that were used in the previous layer.
Example:
A->B->(C,D) and D isn't used then AFAIU they will compile A with B and C.

In general the java rules are much more robust (and complicated) since they have more time/people/history behind it.

I agree I'd like less configuration but I have to say that the current default is mainly because it's what Stripe uses and Stripe started the project. It doesn't mean direct-only is the right mode and I'll even add that all of the google people I talked to thought it's unreasonable (see this discussion for a bit info).

Adding things to scalac might be a good long term goal but we can't hold our breath for it.

I'll add that as far as we're concerned we can definitely discuss removing the current implementation of strict-deps

@ittaiz
Copy link
Member Author

ittaiz commented Feb 21, 2019

I’ll also add that e2e indeed aren’t multiplied but the regular test/... are multiplied

Lastly I’d encourage many different approaches from different companies in hopes we’ll find a good one. From my examples you can see the direct mode breaks on simple usecases. People can get used to adding transitive dependencies without understanding but I don’t think it’s something we want to aspire to you.

@ittaiz
Copy link
Member Author

ittaiz commented Feb 21, 2019

I remembered one more thing about java rules- AFAIR they have one action which gets all of the transitive classpath to it but the differences from our current strict deps approach are:

  1. They try to compile with the +1 variation I explained above and if fail they compile with all of it (as a single bazel action)
  2. Their strict deps analysis mechanism is vastly superior to ours due to many reasons (javac/java simpler, conventions enforced like no wildcards imports, a lot of time/people/experience)

@ittaiz ittaiz closed this Feb 21, 2019
@ittaiz
Copy link
Member Author

ittaiz commented Feb 21, 2019

Closed by mistake

@ittaiz ittaiz reopened this Feb 21, 2019
@ittaiz
Copy link
Member Author

ittaiz commented Feb 22, 2019

@johnynek ?

Copy link
Member

@johnynek johnynek left a comment

Choose a reason for hiding this comment

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

a couple of questions, but I'm mostly positive here.

.travis.yml Outdated Show resolved Hide resolved
scala/private/common.bzl Outdated Show resolved Hide resolved
@@ -1006,36 +1013,6 @@ def scala_test_impl(ctx):
jars.jars2labels,
)

# _scalatest is an http_jar, so its compile jar is run through ijar
Copy link
Member

Choose a reason for hiding this comment

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

why is this all removed now? Is this because we shouldn't depend on a test anyway?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question.
I have an answer which I’m fairly confident about but unfortunately not 100%.
The answer is that since I added the scalatest label as part of the base classpath it’s jars and labels are collected in the main flow.
I have to say that no tests failed even before I did this so I think this area of scalatest and integration with strict-deps/unused deps isn’t tested well.

Lastly tests can depend on tests, not a problem also in java.

test_rules_scala.sh Outdated Show resolved Hide resolved
@ittaiz
Copy link
Member Author

ittaiz commented Feb 22, 2019

@johnynek replied to everything. Feel free to reopen if you feel I've missed something.
Also not 100% sure about the documentation I added so open for feedback.

@ittaiz
Copy link
Member Author

ittaiz commented Feb 25, 2019

@johnynek wdyt? Also the breakage is due to jmh reproducibility flakiness

@johnynek
Copy link
Member

@ittaiz there was a recent change the jmh rules: #695 I wonder if that could be causing the issue.

Copy link
Member

@johnynek johnynek left a comment

Choose a reason for hiding this comment

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

Thanks for working on this, @ittaiz

scala/plusone.bzl Outdated Show resolved Hide resolved
@@ -22,21 +23,25 @@ def collect_srcjars(targets):
def collect_jars(
dep_targets,
dependency_analyzer_is_off = True,
unused_dependency_checker_is_off = True):
unused_dependency_checker_is_off = True,
plus_one_deps_is_off = True):
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should have so many default values here. especially with the same type... but we can punt that concern I guess.

Copy link
Member Author

Choose a reason for hiding this comment

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

I definitely agree...
I also thought about it and the worst thing is that they are all negative case.
I think we still need to do a lot of refactoring here but it's hard to keep up, fix bugs, refactor while also doing all of the other d2d stuff at work...
So thank you also for taking a bigger part of the work of this repo. I hope to remove some of my responsibilities in the next few months so I can give some TLC back here (and maybe also get 1-2 people from Wix to work here some more)

return struct(
compile_jars = depset(transitive = compile_jars),
transitive_runtime_jars = depset(transitive = runtime_jars),
jars2labels = JarsToLabelsInfo(jars_to_labels = jars2labels),
transitive_compile_jars = depset(),
Copy link
Member

Choose a reason for hiding this comment

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

huh, this is interesting. This is a change from before. We weren't really tracking transitive compile jars?

Copy link
Member Author

Choose a reason for hiding this comment

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

if strict deps was off we didn't track it

@ittaiz
Copy link
Member Author

ittaiz commented Feb 25, 2019

re recent change in jmh- don't think it's related since my branch predates it

@johnynek
Copy link
Member

👍

@ittaiz ittaiz merged commit 7ea9079 into bazelbuild:master Feb 25, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants