async def compile_java_source( bash: BashBinary, jdk_setup: JdkSetup, zip_binary: ZipBinary, union_membership: UnionMembership, request: CompileJavaSourceRequest, ) -> FallibleClasspathEntry: # Request the component's direct dependency classpath. direct_dependency_classpath_entries = FallibleClasspathEntry.if_all_succeeded( await MultiGet( Get( FallibleClasspathEntry, ClasspathEntryRequest, ClasspathEntryRequest.for_targets(union_membership, component=coarsened_dep, resolve=request.resolve), ) for coarsened_dep in request.component.dependencies)) if direct_dependency_classpath_entries is None: return FallibleClasspathEntry( description=str(request.component), result=CompileResult.DEPENDENCY_FAILED, output=None, exit_code=1, ) # Then collect the component's sources. component_members_with_sources = tuple(t for t in request.component.members if t.has_field(SourcesField)) component_members_and_source_files = zip( component_members_with_sources, await MultiGet( Get( SourceFiles, SourceFilesRequest( (t.get(SourcesField), ), for_sources_types=(JavaSourceField, ), enable_codegen=True, ), ) for t in component_members_with_sources), ) component_members_and_java_source_files = [ (target, sources) for target, sources in component_members_and_source_files if sources.snapshot.digest != EMPTY_DIGEST ] if not component_members_and_java_source_files: # Is a generator, and so exports all of its direct deps. exported_digest = await Get( Digest, MergeDigests(cpe.digest for cpe in direct_dependency_classpath_entries)) classpath_entry = ClasspathEntry.merge( exported_digest, direct_dependency_classpath_entries) return FallibleClasspathEntry( description=str(request.component), result=CompileResult.SUCCEEDED, output=classpath_entry, exit_code=0, ) dest_dir = "classfiles" (merged_direct_dependency_classpath_digest, dest_dir_digest) = await MultiGet( Get( Digest, MergeDigests( classfiles.digest for classfiles in direct_dependency_classpath_entries), ), Get( Digest, CreateDigest([Directory(dest_dir)]), ), ) usercp = "__cp" prefixed_direct_dependency_classpath_digest = await Get( Digest, AddPrefix(merged_direct_dependency_classpath_digest, usercp)) classpath_arg = ClasspathEntry.arg(direct_dependency_classpath_entries, prefix=usercp) merged_digest = await Get( Digest, MergeDigests(( prefixed_direct_dependency_classpath_digest, dest_dir_digest, jdk_setup.digest, *(sources.snapshot.digest for _, sources in component_members_and_java_source_files), )), ) # Compile. compile_result = await Get( FallibleProcessResult, Process( argv=[ *jdk_setup.args(bash, [f"{jdk_setup.java_home}/lib/tools.jar"]), "com.sun.tools.javac.Main", *(("-cp", classpath_arg) if classpath_arg else ()), "-d", dest_dir, *sorted( chain.from_iterable( sources.snapshot.files for _, sources in component_members_and_java_source_files)), ], input_digest=merged_digest, use_nailgun=jdk_setup.digest, append_only_caches=jdk_setup.append_only_caches, env=jdk_setup.env, output_directories=(dest_dir, ), description=f"Compile {request.component} with javac", level=LogLevel.DEBUG, ), ) if compile_result.exit_code != 0: return FallibleClasspathEntry.from_fallible_process_result( str(request.component), compile_result, None, ) # Jar. # NB: We jar up the outputs in a separate process because the nailgun runner cannot support # invoking via a `bash` wrapper (since the trailing portion of the command is executed by # the nailgun server). We might be able to resolve this in the future via a Javac wrapper shim. output_snapshot = await Get(Snapshot, Digest, compile_result.output_digest) output_file = f"{request.component.representative.address.path_safe_spec}.jar" if output_snapshot.files: jar_result = await Get( ProcessResult, Process( argv=[ bash.path, "-c", " ".join([ "cd", dest_dir, ";", zip_binary.path, "-r", f"../{output_file}", "." ]), ], input_digest=compile_result.output_digest, output_files=(output_file, ), description=f"Capture outputs of {request.component} for javac", level=LogLevel.TRACE, ), ) jar_output_digest = jar_result.output_digest else: # If there was no output, then do not create a jar file. This may occur, for example, when compiling # a `package-info.java` in a single partition. jar_output_digest = EMPTY_DIGEST return FallibleClasspathEntry.from_fallible_process_result( str(request.component), compile_result, ClasspathEntry(jar_output_digest, (output_file, ), direct_dependency_classpath_entries), )
async def compile_scala_source( bash: BashBinary, coursier: Coursier, jdk_setup: JdkSetup, scala: ScalaSubsystem, union_membership: UnionMembership, request: CompileScalaSourceRequest, ) -> FallibleClasspathEntry: # Request classpath entries for our direct dependencies. direct_dependency_classpath_entries = FallibleClasspathEntry.if_all_succeeded( await MultiGet( Get( FallibleClasspathEntry, ClasspathEntryRequest, ClasspathEntryRequest.for_targets(union_membership, component=coarsened_dep, resolve=request.resolve), ) for coarsened_dep in request.component.dependencies)) if direct_dependency_classpath_entries is None: return FallibleClasspathEntry( description=str(request.component), result=CompileResult.DEPENDENCY_FAILED, output=None, exit_code=1, ) component_members_with_sources = tuple(t for t in request.component.members if t.has_field(SourcesField)) component_members_and_source_files = zip( component_members_with_sources, await MultiGet( Get( SourceFiles, SourceFilesRequest( (t.get(SourcesField), ), for_sources_types=(ScalaSourceField, ), enable_codegen=True, ), ) for t in component_members_with_sources), ) component_members_and_scala_source_files = [ (target, sources) for target, sources in component_members_and_source_files if sources.snapshot.digest != EMPTY_DIGEST ] if not component_members_and_scala_source_files: # Is a generator, and so exports all of its direct deps. exported_digest = await Get( Digest, MergeDigests(cpe.digest for cpe in direct_dependency_classpath_entries)) classpath_entry = ClasspathEntry.merge( exported_digest, direct_dependency_classpath_entries) return FallibleClasspathEntry( description=str(request.component), result=CompileResult.SUCCEEDED, output=classpath_entry, exit_code=0, ) ( tool_classpath, merged_transitive_dependency_classpath_entries_digest, ) = await MultiGet( Get( MaterializedClasspath, MaterializedClasspathRequest( prefix="__toolcp", artifact_requirements=(ArtifactRequirements([ Coordinate( group="org.scala-lang", artifact="scala-compiler", version=scala.version, ), Coordinate( group="org.scala-lang", artifact="scala-library", version=scala.version, ), ]), ), ), ), Get( Digest, # Flatten the entire transitive classpath. MergeDigests(classfiles.digest for classfiles in ClasspathEntry.closure( direct_dependency_classpath_entries)), ), ) usercp = "__cp" prefixed_transitive_dependency_classpath_digest = await Get( Digest, AddPrefix(merged_transitive_dependency_classpath_entries_digest, usercp)) merged_digest = await Get( Digest, MergeDigests(( prefixed_transitive_dependency_classpath_digest, tool_classpath.digest, jdk_setup.digest, *(sources.snapshot.digest for _, sources in component_members_and_scala_source_files), )), ) classpath_arg = ClasspathEntry.arg( ClasspathEntry.closure(direct_dependency_classpath_entries), prefix=usercp) output_file = f"{request.component.representative.address.path_safe_spec}.jar" process_result = await Get( FallibleProcessResult, Process( argv=[ *jdk_setup.args(bash, tool_classpath.classpath_entries()), "scala.tools.nsc.Main", "-bootclasspath", ":".join(tool_classpath.classpath_entries()), *(("-classpath", classpath_arg) if classpath_arg else ()), "-d", output_file, *sorted( chain.from_iterable( sources.snapshot.files for _, sources in component_members_and_scala_source_files)), ], input_digest=merged_digest, use_nailgun=jdk_setup.digest, output_files=(output_file, ), description=f"Compile {request.component} with scalac", level=LogLevel.DEBUG, append_only_caches=jdk_setup.append_only_caches, env=jdk_setup.env, ), ) output: ClasspathEntry | None = None if process_result.exit_code == 0: output = ClasspathEntry(process_result.output_digest, (output_file, ), direct_dependency_classpath_entries) return FallibleClasspathEntry.from_fallible_process_result( str(request.component), process_result, output, )