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

Moved to java_common.compile #269

Merged
merged 10 commits into from
Sep 2, 2017

Conversation

ittaiz
Copy link
Member

@ittaiz ittaiz commented Aug 28, 2017

Was supposed to be a refactoring but found a few missing tests.
The good news are that we indeed have good coverage since this hits many code paths and the tests gave me good (though long) feedback about what I'm breaking.

Possible open issues:

  1. I haven't changed the "files" being returned- No tests demanded it and I'm trying to understand what needs this. I think our internal aspect actually will be broken by this so I'll try to add a test like it today and see if it breaks.
  2. Not sure Intellij integration continues to work now- will see how I triage this. @chaoren can the intellij aspect try to use the java_provider before falling back to scala attr?

@ittaiz
Copy link
Member Author

ittaiz commented Aug 28, 2017

fixes #164

@ittaiz ittaiz requested a review from johnynek August 28, 2017 09:43
@ittaiz
Copy link
Member Author

ittaiz commented Aug 28, 2017

@or-shachar @natansil I'd appreciate your review as well please

@ittaiz
Copy link
Member Author

ittaiz commented Aug 28, 2017

Travis build is green for 0.5.3
HEAD builds are broken again because of travis CI

scala/scala.bzl Outdated
scalaattr = struct(
outputs = rule_outputs,
compile_jars = depset([outputs.class_jar]),
Copy link
Member Author

Choose a reason for hiding this comment

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

@johnynek is there any reason you use here outputs.class_jar and in _lib you use ctx.outputs.jar?
I did a few prints and they seem identical.

Copy link
Member Author

Choose a reason for hiding this comment

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

@johnynek ping

Copy link
Member

Choose a reason for hiding this comment

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

I don't think there was a plan. I think it probably was just ill-unified.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, it looks like outputs.class_jar is always ctx.outputs.jar in all cases. We could probably remove it and only use ctx.outputs.jar to be more clear.

Copy link
Member Author

Choose a reason for hiding this comment

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

Decided to leave it for now. Since the outputs.class_jar (although the unclear name) adds symmetry to the collection of the java jar.
We'll handle it when we'll do a bigger refactoring and re-alignment

@@ -614,31 +670,33 @@ def _scala_binary_common(ctx, cjars, rjars, transitive_compile_time_jars, jars2l
files=depset([ctx.outputs.executable]),
Copy link
Member Author

Choose a reason for hiding this comment

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

@johnynek can we add here the actual jars? is there a reason to only return the executable?

Copy link
Member Author

Choose a reason for hiding this comment

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

@johnynek ping

Copy link
Member

Choose a reason for hiding this comment

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

I'm not really sure what needs to be there actually. Since scala_binary creates a default executable, maybe it should only be the executable? Maybe it doesn't matter. Honestly, some of the bazel stuff is not super well documented and I wrote some of this about 2 years ago now and it might have both changed in bazel and also I may have misunderstood it at the time.

I'm afraid I don't have a great answer for you as to what should be in there.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is what the java binary does so I'm guessing it's modeled after that.
We found a workaround so the current status quo is ok

@ittaiz
Copy link
Member Author

ittaiz commented Aug 28, 2017

@chaoren if java_provider isn't an option (though I'd really love to understand why) then I think I can just add an attribute scala.outputs.jars which will have an array of both the scala code and the java code and also add an empty scala.transitive_exports since I don't know what it means and I don't think we need it.
From poking around the aspect those seem to be the only mandatory parts, right?
WDYT in general?

@chaoren
Copy link
Contributor

chaoren commented Aug 29, 2017

@ittaiz

if java_provider isn't an option (though I'd really love to understand why)

We can't easily identify the rule as scala if we're not using scalaattr. It doesn't look like we can refer to the java provider by name (it's just a list of providers), so if another (unsupported) rule provides similar looking providers we can't tell them apart.

I think I can just add an attribute scala.outputs.jars which will have an array of both the scala code and the java code and also add an empty scala.transitive_exports since I don't know what it means and I don't think we need it.

Yes that seems fine.

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.

I'm excited about this. Some clarifying questions.

Thanks for pushing on this!

classes
}

private def discoverClassesIn(archivePath: String) = {
Copy link
Member

Choose a reason for hiding this comment

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

can we add a return type here. I think it is Array[Class[_]]

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do

Copy link
Member

Choose a reason for hiding this comment

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

don't see this yet.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

new JarInputStream(new FileInputStream(archivePath))

private def archivePath: String =
System.getProperty("bazel.discover.classes.archive.file.path")
Copy link
Member

Choose a reason for hiding this comment

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

is this going to break anyone, or is this only expected to be set by a rule? If a rule, is it our rule? should we use io.bazel.rules_scala or something to distinguish? Can we add a comment in any case to ease reading the code?

Copy link
Member Author

Choose a reason for hiding this comment

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

Won't break anyone
our JUnit rule
I'm actually thinking of factoring out the JUnit "discover test classes" mechanism (which this is a part of) since it's not scala related but JUnit related.
When we added this we couldn't do this refactoring due to how scala and java interoped. Due to this I'd rather not change the name.
Re the comment- If one will search the repo for this text he will find it in the bzl so I think it's ok for now but if you feel strongly about it I'll add the comment

Copy link
Member

Choose a reason for hiding this comment

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

I would like to add the comment to explain the magic string because honestly, I was foggy when I looked back at it. This well help someone understand how it was set.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

scala/scala.bzl Outdated
scala_sources_java_provider = java_common.create_provider(
#the line below means we can't use the provider from java_common.compile
#since it will include the full jar when I want to expose only the ijar
#on the other hand we can't supply the java_common.compile the scala ijar since it needs the internals
Copy link
Member

Choose a reason for hiding this comment

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

I don't know what internals mean here. I'm not sure why the ijar of the scalac compilation along with all the ijars we need for compilation is not sufficient. Can you add to the comment to possibly explain this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure but maybe let's first see I'm not just mistaken :) : Won't ijar strip parts from the scalac jar the java sources need? They're the same compilation "group"

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a test can be devised for this...

Copy link
Member

Choose a reason for hiding this comment

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

ijar strips the code and leaves the classes and the signatures. Imagine all methods were replaced with throw new Exception(""). I don't see how javac can use that code at compile time except as annotation processors (which are the closest things java has to macros as far as I know).

So, I think using the ijar is exactly what we want since bazel is now driving this compilation. There could be times where I think bazel could recompile scala, but see that the java didn't change and the compiled ijar didn't change.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good. I'll use the ijar.
I wonder if I should add a no recompilation for same target when scala
changes and java doesn't recompile.
I'll probably add it

Copy link
Member

Choose a reason for hiding this comment

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

👍

Sounds good. Would be nice to see a test that we can change some private method contents and not recompile the java if we can test that.

Copy link
Member Author

Choose a reason for hiding this comment

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

done

java_toolchain = ctx.attr._java_toolchain,
host_javabase = ctx.attr._host_javabase,
)
return struct(jar = full_java_jar, ijar = provider.compile_jars.to_list().pop())
Copy link
Member

Choose a reason for hiding this comment

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

is there really a contract that the first item is the ijar for this item? Can we add a comment to that effect?

This jar only includes the java output, not the scala, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

See the comment 5 lines above. Is it clear enough?
Yes.

Copy link
Member

Choose a reason for hiding this comment

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

yes that's fine.

scala/scala.bzl Outdated
# We assume that if a srcjar is present, it is not empty
if len(ctx.files.srcs) + len(srcjars) == 0:
_build_nosrc_jar(ctx, buildijar)
# no need to build ijar when empty
return struct(ijar=ctx.outputs.jar, class_jar=ctx.outputs.jar)
return struct(ijar=ctx.outputs.jar, class_jar=ctx.outputs.jar, java_jar = False)
Copy link
Member

Choose a reason for hiding this comment

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

wait, here this is a bool, but below it is a struct, I think. Could we use java_jar = None here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do. In python it's the same though I don't know what's the idiomatic way.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@ittaiz
Copy link
Member Author

ittaiz commented Aug 30, 2017

@chaoren I hope it's ok I'm asking but what does a jvm rule (kotlin for example) need for it to be supported? Other than exposing java provider.

Copy link
Contributor

@natansil natansil left a comment

Choose a reason for hiding this comment

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

Looks like this code does the job that it intends to do,
But we need to find a way to have better modularization and abstraction of scala.bzl...

as a first step, maybe split collection, compilation and post-compilation to different files

scala/scala.bzl Outdated
prefixesFlag = "-Dbazel.discover.classes.prefixes=%s" % ",".join(ctx.attr.prefixes),
suffixesFlag = "-Dbazel.discover.classes.suffixes=%s" % ",".join(ctx.attr.suffixes),
printFlag = "-Dbazel.discover.classes.print.discovered=%s" % ctx.attr.print_discovered_classes)

def _get_archives_short_path(scala_archive, maybe_java_archive):
java_archive_short_path = ""
if maybe_java_archive:
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe instead of this if, you can pass this method a list of archives, and some how archives that are None will not get into the list...

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 have a feeling it's indirection and not abstraction. There are just two instances and if I understand correctly you're suggestion is to still have the if just for a "generic" item, right?

Copy link
Member Author

@ittaiz ittaiz Sep 2, 2017

Choose a reason for hiding this comment

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

ended up doing it for a different reason, it allowed me to pass a more generic out argument from the _scala_binary_common

scala/scala.bzl Outdated
scala_sources_java_provider = java_common.create_provider(
#the line below means we can't use the provider from java_common.compile
#since it will include the full jar when I want to expose only the ijar
#on the other hand we can't supply the java_common.compile the scala ijar since it needs the internals
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe a test can be devised for this...

@chaoren
Copy link
Contributor

chaoren commented Aug 31, 2017

@chaoren I hope it's ok I'm asking but what does a jvm rule (kotlin for example) need for it to be supported? Other than exposing java provider.

Technically, we just need to be able to recognize and handle it in the aspect. Do you mean if we decide to support the rule? Then it's probably based on popularity and if we decide it's worth the maintenance cost.

@ittaiz
Copy link
Member Author

ittaiz commented Aug 31, 2017 via email

@ittaiz
Copy link
Member Author

ittaiz commented Aug 31, 2017

@johnynek apart from the ijar change and the IntelliJ change, are we good?
I'd like to merge it tomorrow.
Next change is to add support for strict deps (we first need @iirina to allow passing warn/default from skylark)

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.

this is looking great. I'm really excited. I'm +1 on this, just would like a couple of comments added.

Sorry I have lost some context on your questions.

classes
}

private def discoverClassesIn(archivePath: String) = {
Copy link
Member

Choose a reason for hiding this comment

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

don't see this yet.

new JarInputStream(new FileInputStream(archivePath))

private def archivePath: String =
System.getProperty("bazel.discover.classes.archive.file.path")
Copy link
Member

Choose a reason for hiding this comment

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

I would like to add the comment to explain the magic string because honestly, I was foggy when I looked back at it. This well help someone understand how it was set.

scala/scala.bzl Outdated
scala_sources_java_provider = java_common.create_provider(
#the line below means we can't use the provider from java_common.compile
#since it will include the full jar when I want to expose only the ijar
#on the other hand we can't supply the java_common.compile the scala ijar since it needs the internals
Copy link
Member

Choose a reason for hiding this comment

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

👍

Sounds good. Would be nice to see a test that we can change some private method contents and not recompile the java if we can test that.

java_toolchain = ctx.attr._java_toolchain,
host_javabase = ctx.attr._host_javabase,
)
return struct(jar = full_java_jar, ijar = provider.compile_jars.to_list().pop())
Copy link
Member

Choose a reason for hiding this comment

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

yes that's fine.

scala/scala.bzl Outdated
scalaattr = struct(
outputs = rule_outputs,
compile_jars = depset([outputs.class_jar]),
Copy link
Member

Choose a reason for hiding this comment

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

I don't think there was a plan. I think it probably was just ill-unified.

@@ -614,31 +670,33 @@ def _scala_binary_common(ctx, cjars, rjars, transitive_compile_time_jars, jars2l
files=depset([ctx.outputs.executable]),
Copy link
Member

Choose a reason for hiding this comment

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

I'm not really sure what needs to be there actually. Since scala_binary creates a default executable, maybe it should only be the executable? Maybe it doesn't matter. Honestly, some of the bazel stuff is not super well documented and I wrote some of this about 2 years ago now and it might have both changed in bazel and also I may have misunderstood it at the time.

I'm afraid I don't have a great answer for you as to what should be in there.

scala/scala.bzl Outdated
scalaattr = struct(
outputs = rule_outputs,
compile_jars = depset([outputs.class_jar]),
Copy link
Member

Choose a reason for hiding this comment

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

yeah, it looks like outputs.class_jar is always ctx.outputs.jar in all cases. We could probably remove it and only use ctx.outputs.jar to be more clear.


def _scala_repl_impl(ctx):
# need scala-compiler for MainGenericRunner below
jars = _collect_jars_from_common_ctx(ctx, extra_runtime_deps = [ctx.attr._scalacompiler])
(cjars, transitive_rjars) = (jars.compile_jars, jars.transitive_runtime_jars)
transitive_rjars += [ctx.outputs.jar]
Copy link
Member

Choose a reason for hiding this comment

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

this is removed because _scala_binary_common is putting it in also, is that right?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes

"manifest": "%{name}_MANIFEST.MF",
}

library_outputs = common_outputs + {
Copy link
Member

Choose a reason for hiding this comment

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

nice cleanup.

@johnynek
Copy link
Member

johnynek commented Sep 2, 2017 via email

@ittaiz ittaiz force-pushed the java_common_compile branch from e4baf85 to 8307307 Compare September 2, 2017 14:01
@ittaiz
Copy link
Member Author

ittaiz commented Sep 2, 2017

@johnynek I've handled the return type, the comment, the use of the scala ijar to java siblings and exposing java jars to intellij

@ittaiz
Copy link
Member Author

ittaiz commented Sep 2, 2017

Build is green on 0.5.3

@johnynek
Copy link
Member

johnynek commented Sep 2, 2017

pro-style work sir!

Thanks for pushing this all the way through.

@johnynek johnynek merged commit 1da7e63 into bazelbuild:master Sep 2, 2017
@ittaiz
Copy link
Member Author

ittaiz commented Sep 2, 2017

:) Sure thing. Thank you for your inputs, I think they improved the end result.
Next up I'll open a PR to support strict java deps, I'm just waiting for bazelbuild/bazel#3626 since currently warn/default aren't supported so it will break the build

@ittaiz
Copy link
Member Author

ittaiz commented Sep 2, 2017

btw, I actually think squashing here wasn't the best approach since I tried keeping the commits pretty separated. The benefit from not squashing is smaller commits to comb when going through the history.

@johnynek
Copy link
Member

johnynek commented Sep 2, 2017

you and I seem to disagree about squashed commits. :)

Ultimately, I like one atomic commit of the reviewed and accepted change, and I like to keep each change very small (ideally much under 400 lines).

In your case, for instance, we could have done all the clean up changes in a separate PR (which may have been 1/3 of the diff at the end of the day).

Maybe we should hash out some standard and document it in the repo.

@johnynek
Copy link
Member

johnynek commented Sep 2, 2017

To be clear: this diff was a good size, but by making each reviewed PR atomic and as small as possible, we never have any confusion that any sha in master was not previous state which was reviewed. I know people disagree about this.

Honestly, merge commits might be fine if the git tooling was clearer and easier to use to make it clear that there is no valid view of master that steps into these commits.

@ittaiz
Copy link
Member Author

ittaiz commented Sep 2, 2017 via email

@ittaiz ittaiz deleted the java_common_compile branch September 2, 2017 20:21
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.

5 participants