/* * Copyright 2015-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.maven; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.jvm.java.HasMavenCoordinates; import com.facebook.buck.jvm.java.MavenPublishable; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.rules.AbstractBuildRuleWithResolver; import com.facebook.buck.rules.AddToRuleKey; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.ExplicitBuildTargetSourcePath; import com.facebook.buck.rules.FakeBuildRuleParamsBuilder; import com.facebook.buck.rules.FakeSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.step.Step; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import java.io.BufferedWriter; import java.io.IOException; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collections; import java.util.List; import java.util.Optional; import javax.annotation.Nullable; import javax.xml.transform.TransformerException; import org.apache.maven.model.Dependency; import org.apache.maven.model.Developer; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.junit.Rule; import org.junit.Test; import org.xml.sax.SAXException; public class PomIntegrationTest { private static final MavenXpp3Writer MAVEN_XPP_3_WRITER = new MavenXpp3Writer(); private static final MavenXpp3Reader MAVEN_XPP_3_READER = new MavenXpp3Reader(); private static final String URL = "http://example.com"; @Rule public TemporaryPaths tmp = new TemporaryPaths(); private final BuildRuleResolver ruleResolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); private final SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(ruleResolver)); private final ProjectFilesystem filesystem = FakeProjectFilesystem.createRealTempFilesystem(); @Test public void testMultipleInvocation() throws Exception { // Setup: deps: com.example:with-deps:jar:1.0 -> com.othercorp:no-deps:jar:1.0 BuildRule dep = createMavenPublishable("//example:dep", "com.othercorp:no-deps:1.0", null); MavenPublishable item = createMavenPublishable("//example:has-deps", "com.example:with-deps:1.0", null, dep); Path pomPath = tmp.getRoot().resolve("pom.xml"); assertFalse(Files.exists(pomPath)); // Basic case Pom.generatePomFile(pathResolver, item, pomPath); Model pomModel = parse(pomPath); assertEquals("com.example", pomModel.getGroupId()); assertEquals("with-deps", pomModel.getArtifactId()); assertEquals("1.0", pomModel.getVersion()); List<Dependency> dependencies = pomModel.getDependencies(); assertEquals(1, dependencies.size()); Dependency dependency = dependencies.get(0); assertEquals("com.othercorp", dependency.getGroupId()); assertEquals("no-deps", dependency.getArtifactId()); assertEquals("1.0", dependency.getVersion()); // Corrupt dependency data and ensure buck restores that removeDependencies(pomModel, pomPath); Pom.generatePomFile(pathResolver, item, pomPath); pomModel = parse(pomPath); // Add extra pom data and ensure buck preserves that pomModel.setUrl(URL); serializePom(pomModel, pomPath); Pom.generatePomFile(pathResolver, item, pomPath); pomModel = parse(pomPath); assertEquals(URL, pomModel.getUrl()); } @Test public void shouldUseTemplateIfProvided() throws Exception { MavenPublishable withoutTemplate = createMavenPublishable("//example:no-template", "example.com:project:1.0.0", null); Model noTemplate = parse(Pom.generatePomFile(pathResolver, withoutTemplate)); MavenPublishable withTemplate = createMavenPublishable( "//example:template", "example.com:project:1.0.0", new FakeSourcePath( TestDataHelper.getTestDataDirectory(getClass()) .resolve("poms/template-pom.xml") .toString())); Model templated = parse(Pom.generatePomFile(pathResolver, withTemplate)); // Template sets developers and an example dep. Check that these aren't in the non-templated // version assertTrue(noTemplate.getDevelopers().isEmpty()); assertTrue(noTemplate.getDependencies().isEmpty()); // Now check the same fields in the templated version. Developer seenDev = Iterables.getOnlyElement(templated.getDevelopers()); assertEquals("susan", seenDev.getId()); assertEquals("Susan The Developer", seenDev.getName()); assertEquals(ImmutableList.of("Owner"), seenDev.getRoles()); Dependency seenDep = Iterables.getOnlyElement(templated.getDependencies()); assertEquals("com.google.guava", seenDep.getGroupId()); assertEquals("guava", seenDep.getArtifactId()); assertEquals("19.0", seenDep.getVersion()); } private MavenPublishable createMavenPublishable( String target, String mavenCoords, @Nullable SourcePath pomTemplate, BuildRule... deps) { return ruleResolver.addToIndex( new PublishedViaMaven(target, filesystem, ruleResolver, mavenCoords, pomTemplate, deps)); } private static void serializePom(Model pomModel, Path destination) throws IOException { try (BufferedWriter writer = Files.newBufferedWriter(destination, StandardCharsets.UTF_8)) { MAVEN_XPP_3_WRITER.write(writer, pomModel); } } private static void removeDependencies(Model model, Path pomFile) throws IOException, SAXException, TransformerException { model.setDependencies(Collections.emptyList()); serializePom(model, pomFile); } private static Model parse(Path pomFile) throws Exception { assertTrue(Files.isRegularFile(pomFile)); try (Reader reader = Files.newBufferedReader(pomFile, StandardCharsets.UTF_8)) { return MAVEN_XPP_3_READER.read(reader); } } private static class PublishedViaMaven extends AbstractBuildRuleWithResolver implements MavenPublishable { @Nullable @AddToRuleKey private final SourcePath pomTemplate; @AddToRuleKey private final String coords; public PublishedViaMaven( String target, ProjectFilesystem filesystem, BuildRuleResolver ruleResolver, String coords, @Nullable SourcePath pomTemplate, BuildRule... deps) { super( new FakeBuildRuleParamsBuilder(target) .setDeclaredDeps(ImmutableSortedSet.copyOf(deps)) .setProjectFilesystem(filesystem) .build(), new SourcePathResolver(new SourcePathRuleFinder(ruleResolver))); this.coords = coords; this.pomTemplate = pomTemplate; } @Override public Iterable<HasMavenCoordinates> getMavenDeps() { return FluentIterable.from(getBuildDeps()).filter(HasMavenCoordinates.class); } @Override public Iterable<BuildRule> getPackagedDependencies() { return getDeclaredDeps(); } @Override public Optional<SourcePath> getPomTemplate() { return Optional.ofNullable(pomTemplate); } @Override public Optional<String> getMavenCoords() { return Optional.of(coords); } @Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { return ImmutableList.of(); } @Override public SourcePath getSourcePathToOutput() { return new ExplicitBuildTargetSourcePath( getBuildTarget(), BuildTargets.getGenPath(getProjectFilesystem(), getBuildTarget(), "%s.jar")); } } }