def lockfile() -> CoursierResolvedLockfile: # Calculate transitive deps transitive_ = {(i, k) for i, j in direct.items() for k in j} while True: old_len = len(transitive_) transitive_ |= {(i, k) for i, j in transitive_ for k in direct[j]} if old_len == len(transitive_): break transitive = DefaultDict(set) for (i, j) in transitive_: transitive[i].add(j) entries = ( CoursierLockfileEntry( coord=coord, file_name=f"{coord.artifact}.jar", direct_dependencies=Coordinates(direct[coord]), dependencies=Coordinates(transitive[coord]), file_digest=mock.Mock(), ) for coord in direct ) return CoursierResolvedLockfile(entries=tuple(entries))
def test_fetch_one_coord_with_no_deps(rule_runner: RuleRunner) -> None: classpath_entry = rule_runner.request( ClasspathEntry, [ CoursierLockfileEntry( coord=HAMCREST_COORD, file_name="org.hamcrest_hamcrest-core_1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ) ], ) assert classpath_entry.filenames == ( "org.hamcrest_hamcrest-core_1.3.jar", ) file_digest = rule_runner.request( FileDigest, [ ExtractFileDigest(classpath_entry.digest, "org.hamcrest_hamcrest-core_1.3.jar") ], ) assert file_digest == FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, )
def test_fetch_one_coord_with_transitive_deps(rule_runner: RuleRunner) -> None: junit_coord = Coordinate(group="junit", artifact="junit", version="4.13.2") classpath_entry = rule_runner.request( ClasspathEntry, [ CoursierLockfileEntry( coord=junit_coord, file_name="junit_junit_4.13.2.jar", direct_dependencies=Coordinates([HAMCREST_COORD]), dependencies=Coordinates([HAMCREST_COORD]), file_digest=FileDigest( fingerprint= "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", serialized_bytes_length=384581, ), ) ], ) assert classpath_entry.filenames == ("junit_junit_4.13.2.jar", ) file_digest = rule_runner.request( FileDigest, [ExtractFileDigest(classpath_entry.digest, "junit_junit_4.13.2.jar")]) assert file_digest == FileDigest( fingerprint= "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", serialized_bytes_length=384581, )
def test_resolve_with_transitive_deps(rule_runner: RuleRunner) -> None: junit_coord = Coordinate(group="junit", artifact="junit", version="4.13.2") resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ ArtifactRequirements.from_coordinates([junit_coord]), ], ) assert resolved_lockfile == CoursierResolvedLockfile(entries=( CoursierLockfileEntry( coord=junit_coord, file_name="junit_junit_4.13.2.jar", direct_dependencies=Coordinates([HAMCREST_COORD]), dependencies=Coordinates([HAMCREST_COORD]), file_digest=FileDigest( fingerprint= "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", serialized_bytes_length=384581, ), ), CoursierLockfileEntry( coord=HAMCREST_COORD, file_name="org.hamcrest_hamcrest-core_1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ), ))
def test_generate_lockfile(rule_runner: RuleRunner) -> None: artifacts = ArtifactRequirements([ ArtifactRequirement(Coordinate("org.hamcrest", "hamcrest-core", "1.3")) ]) result = rule_runner.request( GenerateLockfileResult, [ GenerateJvmLockfile(artifacts=artifacts, resolve_name="test", lockfile_dest="lock.txt") ], ) digest_contents = rule_runner.request(DigestContents, [result.digest]) assert len(digest_contents) == 1 expected = CoursierResolvedLockfile( entries=(CoursierLockfileEntry( coord=Coordinate( group="org.hamcrest", artifact="hamcrest-core", version="1.3", ), file_name="org.hamcrest_hamcrest-core_1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ), ), metadata=JVMLockfileMetadata.new(artifacts), ) assert CoursierResolvedLockfile.from_serialized( digest_contents[0].content) == expected
def test_fetch_one_coord_with_mismatched_coord( rule_runner: RuleRunner) -> None: """This test demonstrates that fetch_one_coord is picky about inexact coordinates. Even though the expected jar was downloaded, the coordinate in the lockfile entry was inexact, meaning it wasn't an exact string match for the coordinate fetched and reported by Coursier, which is exact. This shouldn't happen in practice, because these lockfile entries are ultimately derived from Coursier reports which always give exact coordinate strings. """ expected_exception_msg = ( r'Coursier resolved coord.*?"org.hamcrest:hamcrest-core:1.3".*?' r'does not match requested coord.*?"org.hamcrest:hamcrest-core:1.3\+".*?' ) lockfile_entry = CoursierLockfileEntry( coord=Coordinate(group="org.hamcrest", artifact="hamcrest-core", version="1.3+"), file_name="hamcrest-core-1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ) with pytest.raises(ExecutionError, match=expected_exception_msg): rule_runner.request(ClasspathEntry, [lockfile_entry])
def test_resolve_with_inexact_coord(rule_runner: RuleRunner) -> None: resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ # Note the open-ended coordinate here. We will still resolve this for the user, but the result # will be exact and pinned. As noted above, this is an especially brittle unit test, but version # 4.8 was chosen because it has multiple patch versions and no new versions have been uploaded # to 4.8.x in over a decade. ArtifactRequirements.from_coordinates( [Coordinate(group="junit", artifact="junit", version="4.8+")]), ], ) assert resolved_lockfile == CoursierResolvedLockfile( entries=(CoursierLockfileEntry( coord=Coordinate(group="junit", artifact="junit", version="4.8.2"), file_name="junit_junit_4.8.2.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "a2aa2c3bb2b72da76c3e6a71531f1eefdc350494819baf2b1d80d7146e020f9e", serialized_bytes_length=237344, ), ), ))
def test_resolve_with_a_jar(rule_runner: RuleRunner) -> None: rule_runner.write_files({ "BUILD": textwrap.dedent("""\ jvm_artifact( name="jeremy", group="jeremy", artifact="jeremy", version="4.13.2", jar="jeremy.jar", ) """), "jeremy.jar": "hello dave", }) targets = rule_runner.request(Targets, [ RawSpecs(recursive_globs=(RecursiveGlobSpec(""), ), description_of_origin="tests") ]) jeremy_target = targets[0] jar_field = jeremy_target[JvmArtifactJarSourceField] requirement = ArtifactRequirement( coordinate=Coordinate( group="jeremy", artifact="jeremy", version="4.13.2", ), jar=jar_field, ) resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ArtifactRequirements([requirement])], ) coordinate = requirement.coordinate assert resolved_lockfile == CoursierResolvedLockfile( entries=(CoursierLockfileEntry( coord=Coordinate(group=coordinate.group, artifact=coordinate.artifact, version=coordinate.version), file_name= f"{coordinate.group}_{coordinate.artifact}_{coordinate.version}.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "55b9afa8d7776cd6c318eec51f506e9c7f66c247dcec343d4667f5f269714f86", serialized_bytes_length=10, ), pants_address=jar_field.address.spec, ), ))
def test_resolve_with_no_deps(rule_runner: RuleRunner) -> None: resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ArtifactRequirements.from_coordinates([HAMCREST_COORD])], ) assert resolved_lockfile == CoursierResolvedLockfile( entries=(CoursierLockfileEntry( coord=HAMCREST_COORD, file_name="org.hamcrest_hamcrest-core_1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9", serialized_bytes_length=45024, ), ), ))
def test_fetch_one_coord_with_jar(rule_runner: RuleRunner) -> None: coord = Coordinate(group="jeremy", artifact="jeremy", version="4.13.2") file_name = f"{coord.group}_{coord.artifact}_{coord.version}.jar" rule_runner.write_files({ "BUILD": textwrap.dedent(f"""\ jvm_artifact( name="jeremy", group="{coord.group}", artifact="{coord.artifact}", version="{coord.version}", jar="jeremy.jar", ) """), "jeremy.jar": "hello dave", }) classpath_entry = rule_runner.request( ClasspathEntry, [ CoursierLockfileEntry( coord=coord, file_name=file_name, direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "55b9afa8d7776cd6c318eec51f506e9c7f66c247dcec343d4667f5f269714f86", serialized_bytes_length=10, ), pants_address="//:jeremy", ) ], ) assert classpath_entry.filenames == (file_name, ) file_digest = rule_runner.request( FileDigest, [ExtractFileDigest(classpath_entry.digest, file_name)], ) assert file_digest == FileDigest( fingerprint= "55b9afa8d7776cd6c318eec51f506e9c7f66c247dcec343d4667f5f269714f86", serialized_bytes_length=10, )
async def fetch_nailgun() -> Nailgun: nailgun = await Get( ClasspathEntry, CoursierLockfileEntry( coord=Coordinate.from_coord_str( "com.martiansoftware:nailgun-server:0.9.1"), file_name="com.martiansoftware_nailgun-server_0.9.1.jar", direct_dependencies=Coordinates(), dependencies=Coordinates(), file_digest=FileDigest( fingerprint= "4518faa6bf4bd26fccdc4d85e1625dc679381a08d56872d8ad12151dda9cef25", serialized_bytes_length=32927, ), ), ) return Nailgun(nailgun)
def test_fetch_one_coord_with_bad_fingerprint(rule_runner: RuleRunner) -> None: expected_exception_msg = ( r".*?CoursierError:.*?Coursier fetch for .*?hamcrest.*? succeeded.*?" r"66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9.*?" r"did not match.*?ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ) lockfile_entry = CoursierLockfileEntry( coord=HAMCREST_COORD, file_name="hamcrest-core-1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", serialized_bytes_length=45024, ), ) with pytest.raises(ExecutionError, match=expected_exception_msg): rule_runner.request(ClasspathEntry, [lockfile_entry])
def from_json_dict(cls, entry) -> CoursierLockfileEntry: """Construct a CoursierLockfileEntry from its JSON dictionary representation.""" return cls( coord=Coordinate.from_json_dict(entry["coord"]), file_name=entry["file_name"], direct_dependencies=Coordinates( Coordinate.from_json_dict(d) for d in entry["directDependencies"]), dependencies=Coordinates( Coordinate.from_json_dict(d) for d in entry["dependencies"]), file_digest=FileDigest( fingerprint=entry["file_digest"]["fingerprint"], serialized_bytes_length=entry["file_digest"] ["serialized_bytes_length"], ), remote_url=entry.get("remote_url"), pants_address=entry.get("pants_address"), )
def test_resolve_with_working_url(rule_runner: RuleRunner) -> None: requirement = ArtifactRequirement( coordinate=Coordinate( group="apache-commons-local", artifact="commons-collections", version="1.0.0_JAR_LOCAL", ), url= "https://repo1.maven.org/maven2/org/apache/commons/commons-collections4/4.2/commons-collections4-4.2.jar", ) resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ArtifactRequirements([requirement])], ) coordinate = requirement.coordinate assert resolved_lockfile == CoursierResolvedLockfile( entries=(CoursierLockfileEntry( coord=Coordinate(group=coordinate.group, artifact=coordinate.artifact, version=coordinate.version), file_name= f"{coordinate.group}_{coordinate.artifact}_{coordinate.version}.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "6a594721d51444fd97b3eaefc998a77f606dedb03def494f74755aead3c9df3e", serialized_bytes_length=752798, ), remote_url=requirement.url, pants_address=None, ), ))
def test_compile_with_maven_deps(rule_runner: RuleRunner) -> None: joda_coord = Coordinate(group="joda-time", artifact="joda-time", version="2.10.10") scala_library_coord = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.6") resolved_joda_lockfile = TestCoursierWrapper.new(entries=( CoursierLockfileEntry( coord=joda_coord, file_name="joda-time-2.10.10.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "dd8e7c92185a678d1b7b933f31209b6203c8ffa91e9880475a1be0346b9617e3", serialized_bytes_length=644419, ), ), CoursierLockfileEntry( coord=scala_library_coord, file_name="org.scala-lang_scala-library_2.13.6.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a", 5955737, ), ), )) rule_runner.write_files({ "BUILD": dedent(f"""\ jvm_artifact( name = "joda-time_joda-time", group = "{joda_coord.group}", artifact = "{joda_coord.artifact}", version = "{joda_coord.version}", ) scala_sources( name = 'main', dependencies = [":joda-time_joda-time"], ) """), "3rdparty/jvm/BUILD": DEFAULT_SCALA_LIBRARY_TARGET, "3rdparty/jvm/default.lock": resolved_joda_lockfile.serialize([ ArtifactRequirement(joda_coord), ArtifactRequirement(scala_library_coord) ]), "Example.scala": dedent(""" package org.pantsbuild.example import org.joda.time.DateTime object Main { def main(args: Array[String]): Unit = { val dt = new DateTime() println(dt.getYear) } } """), }) classpath = rule_runner.request( RenderedClasspath, [ CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main")), resolve=make_resolve(rule_runner), ) ], ) assert classpath.content == { ".Example.scala.main.scalac.jar": { "META-INF/MANIFEST.MF", "org/pantsbuild/example/Main$.class", "org/pantsbuild/example/Main.class", } }
def test_resolve_with_packaging(rule_runner: RuleRunner) -> None: # Tests that an artifact pom which actually reports packaging ends up with proper version and # packaging information. # see https://github.com/pantsbuild/pants/issues/13986 resolved_lockfile = rule_runner.request( CoursierResolvedLockfile, [ ArtifactRequirements.from_coordinates([ Coordinate(group="org.bouncycastle", artifact="bcutil-jdk15on", version="1.70") ]), ], ) assert resolved_lockfile == CoursierResolvedLockfile(entries=( CoursierLockfileEntry( coord=Coordinate( group="org.bouncycastle", artifact="bcprov-jdk15on", version="1.70", packaging="jar", strict=True, ), file_name="org.bouncycastle_bcprov-jdk15on_jar_1.70.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "8f3c20e3e2d565d26f33e8d4857a37d0d7f8ac39b62a7026496fcab1bdac30d4", 5867298), remote_url=None, pants_address=None, ), CoursierLockfileEntry( coord=Coordinate( group="org.bouncycastle", artifact="bcutil-jdk15on", version="1.70", packaging="jar", strict=True, ), file_name="org.bouncycastle_bcutil-jdk15on_1.70.jar", direct_dependencies=Coordinates([ Coordinate( group="org.bouncycastle", artifact="bcprov-jdk15on", version="1.70", packaging="jar", strict=True, ) ]), dependencies=Coordinates([ Coordinate( group="org.bouncycastle", artifact="bcprov-jdk15on", version="1.70", packaging="jar", strict=True, ) ]), file_digest=FileDigest( "52dc5551b0257666526c5095424567fed7dc7b00d2b1ba7bd52298411112b1d0", 482530), remote_url=None, pants_address=None, ), ))
async def coursier_resolve_lockfile( artifact_requirements: ArtifactRequirements, ) -> CoursierResolvedLockfile: """Run `coursier fetch ...` against a list of Maven coordinates and capture the result. This rule does two things in a single Process invocation: * Runs `coursier fetch` to let Coursier do the heavy lifting of resolving dependencies and downloading resolved artifacts (jars, etc). * Copies the resolved artifacts into the Process output directory, capturing the artifacts as content-addressed `Digest`s. It's important that this happens in the same process, since the process isn't guaranteed to run on the same machine as the rule, nor is a subsequent process invocation. This guarantees that whatever Coursier resolved, it was fully captured into Pants' content addressed artifact storage. Note however that we still get the benefit of Coursier's "global" cache if it had already been run on the machine where the `coursier fetch` runs, so rerunning `coursier fetch` tends to be fast in practice. Finally, this rule bundles up the result into a `CoursierResolvedLockfile`. This data structure encapsulates everything necessary to either materialize the resolved dependencies to a classpath for Java invocations, or to write the lockfile out to the workspace to hermetically freeze the result of the resolve. """ if len(artifact_requirements) == 0: return CoursierResolvedLockfile(entries=()) coursier_resolve_info = await Get(CoursierResolveInfo, ArtifactRequirements, artifact_requirements) coursier_report_file_name = "coursier_report.json" process_result = await Get( ProcessResult, CoursierFetchProcess( args=( coursier_report_file_name, *coursier_resolve_info.argv, ), input_digest=coursier_resolve_info.digest, output_directories=("classpath", ), output_files=(coursier_report_file_name, ), description= ("Running `coursier fetch` against " f"{pluralize(len(artifact_requirements), 'requirement')}: " f"{', '.join(req.to_coord_arg_str() for req in artifact_requirements)}" ), ), ) report_digest = await Get( Digest, DigestSubset(process_result.output_digest, PathGlobs([coursier_report_file_name]))) report_contents = await Get(DigestContents, Digest, report_digest) report = json.loads(report_contents[0].content) artifact_file_names = tuple( classpath_dest_filename(dep["coord"], dep["file"]) for dep in report["dependencies"]) artifact_output_paths = tuple(f"classpath/{file_name}" for file_name in artifact_file_names) artifact_digests = await MultiGet( Get( Digest, DigestSubset(process_result.output_digest, PathGlobs( [output_path]))) for output_path in artifact_output_paths) stripped_artifact_digests = await MultiGet( Get(Digest, RemovePrefix(artifact_digest, "classpath")) for artifact_digest in artifact_digests) artifact_file_digests = await MultiGet( Get(FileDigest, ExtractFileDigest(stripped_artifact_digest, file_name)) for stripped_artifact_digest, file_name in zip( stripped_artifact_digests, artifact_file_names)) first_pass_lockfile = CoursierResolvedLockfile(entries=tuple( CoursierLockfileEntry( coord=Coordinate.from_coord_str(dep["coord"]), direct_dependencies=Coordinates( Coordinate.from_coord_str(dd) for dd in dep["directDependencies"]), dependencies=Coordinates( Coordinate.from_coord_str(d) for d in dep["dependencies"]), file_name=file_name, file_digest=artifact_file_digest, ) for dep, file_name, artifact_file_digest in zip( report["dependencies"], artifact_file_names, artifact_file_digests))) inverted_artifacts = {req.coordinate: req for req in artifact_requirements} new_entries = [] for entry in first_pass_lockfile.entries: req = inverted_artifacts.get(entry.coord) if req: address = req.jar.address if req.jar else None address_spec = address.spec if address else None entry = dataclasses.replace(entry, remote_url=req.url, pants_address=address_spec) new_entries.append(entry) return CoursierResolvedLockfile(entries=tuple(new_entries))
# MavenRequirements.create_from_maven_coordinates_fields( # fields=(), # additional_requirements=["junit:junit:4.13.2"], # ) # ], # ) # The `repr` of the resulting lockfile object can be directly copied # into code to get the following: hamcrest_coord = Coordinate(group="org.hamcrest", artifact="hamcrest-core", version="1.3") JUNIT4_RESOLVED_LOCKFILE = TestCoursierWrapper.new(entries=( CoursierLockfileEntry( coord=JUNIT_COORD, file_name="junit-4.13.2.jar", direct_dependencies=Coordinates([hamcrest_coord]), dependencies=Coordinates([hamcrest_coord]), file_digest=FileDigest( fingerprint= "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", serialized_bytes_length=384581, ), ), CoursierLockfileEntry( coord=hamcrest_coord, file_name="hamcrest-core-1.3.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9",
], target_types=[ JvmArtifactTarget, ScalaSourcesGeneratorTarget, ScalacPluginTarget ], ) rule_runner.set_options(args=[], env_inherit=PYTHON_BOOTSTRAP_ENV) return rule_runner DEFAULT_LOCKFILE = TestCoursierWrapper( CoursierResolvedLockfile((CoursierLockfileEntry( coord=Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.6"), file_name="org.scala-lang_scala-library_2.13.6.jar", direct_dependencies=Coordinates(), dependencies=Coordinates(), file_digest=FileDigest( "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a", 5955737), ), ))).serialize([ ArtifactRequirement(coordinate=Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.6")) ]) DEFAULT_SCALA_LIBRARY_TARGET = textwrap.dedent("""\ jvm_artifact( name="org.scala-lang_scala-library_2.13.6", group="org.scala-lang", artifact="scala-library",
def test_compile_with_multiple_scalac_plugins(rule_runner: RuleRunner) -> None: better_monadic_coord = Coordinate(group="com.olegpy", artifact="better-monadic-for_2.13", version="0.3.1") kind_projector_coord = Coordinate(group="org.typelevel", artifact="kind-projector_2.13.6", version="0.13.2") scala_compiler_coord = Coordinate(group="org.scala-lang", artifact="scala-compiler", version="2.13.6") scala_library_coord = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.6") scala_reflect_coord = Coordinate(group="org.scala-lang", artifact="scala-reflect", version="2.13.6") jna_coord = Coordinate(group="net.java.dev.jna", artifact="jna", version="5.3.1") jline_coord = Coordinate(group="org.jline", artifact="jline", version="3.19.0") rule_runner.write_files({ "lib/BUILD": dedent(f"""\ scala_sources() jvm_artifact( name="kind-projector-lib", group="{kind_projector_coord.group}", artifact="{kind_projector_coord.artifact}", version="{kind_projector_coord.version}", ) scalac_plugin( name="kind-projector", plugin_name="kind-projector", # TODO: Support relative addresses. artifact="lib:kind-projector-lib", ) jvm_artifact( name="better-monadic-for-lib", group="{better_monadic_coord.group}", artifact="{better_monadic_coord.artifact}", version="{better_monadic_coord.version}", ) scalac_plugin( name="better-monadic-for", plugin_name="bm4", # TODO: Support relative addresses. artifact="lib:better-monadic-for-lib", ) """), "3rdparty/jvm/BUILD": DEFAULT_SCALA_LIBRARY_TARGET, "3rdparty/jvm/default.lock": DEFAULT_LOCKFILE, "3rdparty/jvm/scalac-plugins.lock": TestCoursierWrapper.new(entries=( CoursierLockfileEntry( coord=better_monadic_coord, file_name="com.olegpy_better-monadic-for_2.13_0.3.1.jar", direct_dependencies=Coordinates( [scala_compiler_coord, scala_library_coord]), dependencies=Coordinates( [scala_compiler_coord, scala_library_coord]), file_digest=FileDigest( "fac649fa7de697d1f98d3f814c4b70f5372c547fa41778383e22cee6c16084f5", 130370, ), ), CoursierLockfileEntry( coord=jna_coord, file_name="net.java.dev.jna_jna_5.3.1.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "01cb505c0698d0f7acf3524c7e73acb7dc424a5bae5e9c86ce44075ab32bc4ee", 1505196, ), ), CoursierLockfileEntry( coord=jline_coord, file_name="org.jline_jline_3.19.0.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "c99ddcfa5431cab88d1cd40fd63bec6ab5a3fe2e83877051198539af66592a46", 987021, ), ), CoursierLockfileEntry( coord=scala_compiler_coord, file_name="org.scala-lang_scala-compiler_2.13.6.jar", direct_dependencies=Coordinates([ jna_coord, jline_coord, scala_library_coord, scala_reflect_coord ]), dependencies=Coordinates([ jna_coord, jline_coord, scala_library_coord, scala_reflect_coord ]), file_digest=FileDigest( "310d263d622a3d016913e94ee00b119d270573a5ceaa6b21312d69637fd9eec1", 12010571, ), ), CoursierLockfileEntry( coord=scala_library_coord, file_name="org.scala-lang_scala-library_2.13.6.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a", 5955737, ), ), CoursierLockfileEntry( coord=scala_reflect_coord, file_name="org.scala-lang_scala-reflect_2.13.6.jar", direct_dependencies=Coordinates([scala_library_coord]), dependencies=Coordinates([scala_library_coord]), file_digest=FileDigest( "f713593809b387c60935bb9a940dfcea53bd0dbf8fdc8d10739a2896f8ac56fa", 3769997, ), ), CoursierLockfileEntry( coord=kind_projector_coord, file_name="org.typelevel_kind-projector_2.13.6_0.13.2.jar", direct_dependencies=Coordinates( [scala_compiler_coord, scala_library_coord]), dependencies=Coordinates([ scala_compiler_coord, scala_reflect_coord, scala_library_coord, jna_coord, jline_coord, ]), file_digest=FileDigest( "3d713d02bbe0d52b01c22ac11a50970460114f32b339f3ea429d52461d6c39ff", 44257, ), ), )).serialize([ ArtifactRequirement(better_monadic_coord), ArtifactRequirement(kind_projector_coord), ]), "lib/A.scala": dedent("""\ trait Functor[F[_]] { def map[A, B](fa: F[A])(f: A => B): F[B] } object KindProjectorTest { implicit def eitherFunctor[E]: Functor[Either[E, *]] = new Functor[Either[E, *]] { def map[A, B](fa: Either[E, A])(f: A => B): Either[E, B] = { fa match { case Left(e) => Left(e) case Right(a) => Right(f(a)) } } } } object BetterMonadicForTest { def example: Option[String] = { case class ImplicitTest(id: String) for { x <- Option(42) implicit0(it: ImplicitTest) <- Option(ImplicitTest("eggs")) _ <- Option("dummy") _ = "dummy" _ = assert(implicitly[ImplicitTest] eq it) } yield "ok" } } """), }) rule_runner.set_options( args=[ "--scalac-plugins-global=['lib:better-monadic-for', 'lib:kind-projector']", "--scalac-plugins-global-lockfile=3rdparty/jvm/scalac-plugins.lock", ], env_inherit=PYTHON_BOOTSTRAP_ENV, ) request = CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="lib", relative_file_path="A.scala")), resolve=make_resolve(rule_runner), ) rule_runner.request(RenderedClasspath, [request])
def test_compile_with_local_scalac_plugin(rule_runner: RuleRunner) -> None: acyclic_coord = Coordinate(group="com.lihaoyi", artifact="acyclic_2.13", version="0.2.1") scala_library_coord = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.6") rule_runner.write_files({ "lib/BUILD": dedent(f"""\ jvm_artifact( name = "acyclic_lib", group = "{acyclic_coord.group}", artifact = "{acyclic_coord.artifact}", version = "{acyclic_coord.version}", packages=["acyclic.**"], ) scalac_plugin( name = "acyclic", # TODO: Support relative addresses. artifact = "lib:acyclic_lib", ) scala_sources( scalac_plugins=["acyclic"], ) """), "3rdparty/jvm/BUILD": DEFAULT_SCALA_LIBRARY_TARGET, "3rdparty/jvm/default.lock": TestCoursierWrapper.new(entries=( CoursierLockfileEntry( coord=acyclic_coord, file_name="acyclic_2.13-0.2.1.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "4bc4656140ad5e4802fedcdbe920ec7c92dbebf5e76d1c60d35676a314481944", 62534, ), ), CoursierLockfileEntry( coord=scala_library_coord, file_name="org.scala-lang_scala-library_2.13.6.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a", 5955737, ), ), )).serialize([ ArtifactRequirement(coordinate=acyclic_coord), ArtifactRequirement(scala_library_coord), ]), "3rdparty/jvm/scalac-plugins.lock": TestCoursierWrapper.new(entries=( CoursierLockfileEntry( coord=acyclic_coord, file_name="acyclic_2.13-0.2.1.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "4bc4656140ad5e4802fedcdbe920ec7c92dbebf5e76d1c60d35676a314481944", 62534, ), ), CoursierLockfileEntry( coord=scala_library_coord, file_name="org.scala-lang_scala-library_2.13.6.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "f19ed732e150d3537794fd3fe42ee18470a3f707efd499ecd05a99e727ff6c8a", 5955737, ), ), )).serialize([ ArtifactRequirement(coordinate=acyclic_coord), ]), "lib/A.scala": dedent(""" package lib import acyclic.file class A { val b: B = null } """), "lib/B.scala": dedent(""" package lib class B { val a: A = null } """), }) rule_runner.set_options( args=[], env_inherit=PYTHON_BOOTSTRAP_ENV, ) request = CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="lib", relative_file_path="A.scala")), resolve=make_resolve(rule_runner), ) fallible_result = rule_runner.request(FallibleClasspathEntry, [request]) assert fallible_result.result == CompileResult.FAILED and fallible_result.stderr assert "error: Unwanted cyclic dependency" in fallible_result.stderr
def test_basic_war_packaging(rule_runner: RuleRunner) -> None: servlet_coordinate = Coordinate(group="javax.servlet", artifact="servlet-api", version="2.5") rule_runner.write_files( { "war-test/BUILD": textwrap.dedent( """\ jvm_artifact( name="javax.servlet_servlet-api", group="javax.servlet", artifact="servlet-api", version="2.5", ) jvm_war( name="war", dependencies=[":javax.servlet_servlet-api"], descriptor="web.xml", content=[":html"], ) files(name="orig_html", sources=["*.html"]) relocated_files( name="html", files_targets=[":orig_html"], src="war-test", dest="", ) """ ), "war-test/web.xml": textwrap.dedent( """\ <web-app> </web-app> """ ), "war-test/index.html": textwrap.dedent( """\ <html> <body> <p>This is the home page.</p> </html> """ ), "3rdparty/jvm/default.lock": TestCoursierWrapper.new( ( CoursierLockfileEntry( coord=servlet_coordinate, file_name="javax.servlet_servlet-api_2.5.jar", direct_dependencies=Coordinates(), dependencies=Coordinates(), file_digest=FileDigest( "c658ea360a70faeeadb66fb3c90a702e4142a0ab7768f9ae9828678e0d9ad4dc", 105112, ), ), ), ).serialize([ArtifactRequirement(servlet_coordinate)]), } ) war_tgt = rule_runner.get_target(Address("war-test", target_name="war")) built_package = rule_runner.request(BuiltPackage, [PackageWarFileFieldSet.create(war_tgt)]) assert built_package.digest != EMPTY_DIGEST assert len(built_package.artifacts) == 1 package = built_package.artifacts[0] assert package.relpath == "war-test/war.war" contents = rule_runner.request(DigestContents, [built_package.digest]) assert len(contents) == 1 zip_bytes = BytesIO(contents[0].content) with zipfile.ZipFile(zip_bytes, "r") as zf: files = zf.filelist filenames = [f.filename for f in files] assert sorted(filenames) == [ "WEB-INF/", "WEB-INF/classes/", "WEB-INF/lib/", "WEB-INF/lib/javax.servlet_servlet-api_2.5.jar", "WEB-INF/web.xml", "index.html", ]
def test_compile_with_multiple_scala_versions(rule_runner: RuleRunner) -> None: scala_library_coord_2_12 = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.12.15") scala_library_coord_2_13 = Coordinate(group="org.scala-lang", artifact="scala-library", version="2.13.8") rule_runner.write_files({ "BUILD": dedent("""\ scala_sources( name = 'main_2.12', resolve = "scala2.12", ) scala_sources( name = 'main_2.13', resolve = "scala2.13", ) jvm_artifact( name="org.scala-lang_scala-library_2.12.15", group="org.scala-lang", artifact="scala-library", version="2.12.15", resolve="scala2.12", ) jvm_artifact( name="org.scala-lang_scala-library_2.13.8", group="org.scala-lang", artifact="scala-library", version="2.13.8", resolve="scala2.13", ) """), "Example.scala": SCALA_LIB_SOURCE, "3rdparty/jvm/scala2.12.lock": TestCoursierWrapper.new(entries=(CoursierLockfileEntry( coord=scala_library_coord_2_12, file_name="org.scala-lang_scala-library_2.12.15.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "e518bb640e2175de5cb1f8e326679b8d975376221f1b547757de429bbf4563f0", 5443542, ), ), ), ).serialize([ArtifactRequirement(scala_library_coord_2_12)]), "3rdparty/jvm/scala2.13.lock": TestCoursierWrapper.new(entries=(CoursierLockfileEntry( coord=scala_library_coord_2_13, file_name="org.scala-lang_scala-library_2.13.8.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( "a0882b82514190c2bac7d1a459872a75f005fc0f3e88b2bc0390367146e35db7", 6003601, ), ), ), ).serialize([ArtifactRequirement(scala_library_coord_2_13)]), }) rule_runner.set_options( [ '--scala-version-for-resolve={"scala2.12":"2.12.15","scala2.13":"2.13.8"}', '--jvm-resolves={"scala2.12":"3rdparty/jvm/scala2.12.lock","scala2.13":"3rdparty/jvm/scala2.13.lock"}', ], env_inherit=PYTHON_BOOTSTRAP_ENV, ) classpath_2_12 = rule_runner.request( ClasspathEntry, [ CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main_2.12")), resolve=make_resolve(rule_runner, "scala2.12", "3rdparty/jvm/scala2.12.lock"), ) ], ) entries_2_12 = list(ClasspathEntry.closure([classpath_2_12])) filenames_2_12 = sorted( itertools.chain.from_iterable(entry.filenames for entry in entries_2_12)) assert filenames_2_12 == [ ".Example.scala.main_2.12.scalac.jar", "org.scala-lang_scala-library_2.12.15.jar", ] classpath_2_13 = rule_runner.request( ClasspathEntry, [ CompileScalaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main_2.13")), resolve=make_resolve(rule_runner, "scala2.13", "3rdparty/jvm/scala2.13.lock"), ) ], ) entries_2_13 = list(ClasspathEntry.closure([classpath_2_13])) filenames_2_13 = sorted( itertools.chain.from_iterable(entry.filenames for entry in entries_2_13)) assert filenames_2_13 == [ ".Example.scala.main_2.13.scalac.jar", "org.scala-lang_scala-library_2.13.8.jar", ]
def test_compile_with_maven_deps(rule_runner: RuleRunner) -> None: joda_coord = Coordinate(group="joda-time", artifact="joda-time", version="2.10.10") resolved_joda_lockfile = TestCoursierWrapper.new( entries=(CoursierLockfileEntry( coord=joda_coord, file_name="joda-time-2.10.10.jar", direct_dependencies=Coordinates([]), dependencies=Coordinates([]), file_digest=FileDigest( fingerprint= "dd8e7c92185a678d1b7b933f31209b6203c8ffa91e9880475a1be0346b9617e3", serialized_bytes_length=644419, ), ), )) rule_runner.write_files({ "BUILD": dedent("""\ jvm_artifact( name = "joda-time_joda-time", group = "joda-time", artifact = "joda-time", version = "2.10.10", ) java_sources( name = 'main', dependencies = [ ':joda-time_joda-time', ] ) """), "3rdparty/jvm/default.lock": resolved_joda_lockfile.serialize( [ArtifactRequirement(coordinate=joda_coord)]), "Example.java": dedent(""" package org.pantsbuild.example; import org.joda.time.DateTime; public class Example { public static void main(String[] args) { DateTime dt = new DateTime(); System.out.println(dt.getYear()); } } """), }) request = CompileJavaSourceRequest( component=expect_single_expanded_coarsened_target( rule_runner, Address(spec_path="", target_name="main")), resolve=make_resolve(rule_runner), ) classpath = rule_runner.request(RenderedClasspath, [request]) assert classpath.content == { ".Example.java.main.javac.jar": {"org/pantsbuild/example/Example.class"} }