Skip to content

Commit

Permalink
Simplify build plugin license handling (#77009)
Browse files Browse the repository at this point in the history
- Use file property and conventions to avoid afterEvaluate hook
- Simplify root build script
- One little step closer to configuration cache compliance
  • Loading branch information
breskeby authored Oct 7, 2021
1 parent 9e27100 commit dd4e4c3
Show file tree
Hide file tree
Showing 20 changed files with 115 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.initialization.layout.BuildLayout;

import javax.inject.Inject;
import java.io.File;
Expand All @@ -29,7 +28,7 @@ class GitInfoPlugin implements Plugin<Project> {
private Property<GitInfo> gitInfo;

@Inject
public GitInfoPlugin(ProviderFactory factory, ObjectFactory objectFactory) {
GitInfoPlugin(ProviderFactory factory, ObjectFactory objectFactory) {
this.factory = factory;
this.objectFactory = objectFactory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.provider.MapProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.ProviderFactory;

import javax.inject.Inject;
import java.util.Map;
import java.util.concurrent.Callable;

public class LicensingPlugin implements Plugin<Project> {
final static String ELASTIC_LICENSE_URL_PREFIX = "https://raw.githubusercontent.com/elastic/elasticsearch/";
final static String ELASTIC_LICENSE_URL_POSTFIX = "/licenses/ELASTIC-LICENSE-2.0.txt";
static final String ELASTIC_LICENSE_URL_PREFIX = "https://raw.githubusercontent.com/elastic/elasticsearch/";
static final String ELASTIC_LICENSE_URL_POSTFIX = "/licenses/ELASTIC-LICENSE-2.0.txt";

private ProviderFactory providerFactory;

Expand All @@ -34,24 +32,23 @@ public LicensingPlugin(ProviderFactory providerFactory) {
public void apply(Project project) {
Provider<String> revision = project.getRootProject().getPlugins().apply(GitInfoPlugin.class).getRevision();
Provider<String> licenseCommitProvider = providerFactory.provider(() ->
isSnapshotVersion(project) ? revision.get() : "v" + project.getVersion().toString()
isSnapshotVersion(project) ? revision.get() : "v" + project.getVersion()
);

MapProperty<String, String> licensesProperty = project.getObjects().mapProperty(String.class, String.class);
Provider<String> projectLicenseURL = licenseCommitProvider.map(licenseCommit -> ELASTIC_LICENSE_URL_PREFIX +
licenseCommit + ELASTIC_LICENSE_URL_POSTFIX);
// But stick the Elastic license url in project.ext so we can get it if we need to switch to it
project.getExtensions().getExtraProperties().set("elasticLicenseUrl", projectLicenseURL);

MapProperty<String, String> convention = licensesProperty.convention(
providerFactory.provider((Callable<Map<? extends String, ? extends String>>) () -> Map.of(
MapProperty<String, String> licensesProperty = project.getObjects().mapProperty(String.class, String.class).convention(
providerFactory.provider(() -> Map.of(
"Server Side Public License, v 1", "https://www.mongodb.com/licensing/server-side-public-license",
"Elastic License 2.0", projectLicenseURL.get())
)
);

// Default to the SSPL+Elastic dual license
project.getExtensions().getExtraProperties().set("licenseCommit", licenseCommitProvider);
project.getExtensions().getExtraProperties().set("projectLicenses", convention);
project.getExtensions().getExtraProperties().set("projectLicenses", licensesProperty);
}

private boolean isSnapshotVersion(Project project) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,8 @@ class PublishPluginFuncTest extends AbstractGradleFuncTest {
// requires elasticsearch artifact available
tasks.named('bundlePlugin').configure { enabled = false }
licenseFile = file('license.txt')
noticeFile = file('notice.txt')
licenseFile.set(file('license.txt'))
noticeFile.set(file('notice.txt'))
version = "1.0"
group = 'org.acme'
"""
Expand Down Expand Up @@ -352,8 +352,8 @@ class PublishPluginFuncTest extends AbstractGradleFuncTest {
// requires elasticsearch artifact available
tasks.named('bundlePlugin').configure { enabled = false }
licenseFile = file('license.txt')
noticeFile = file('notice.txt')
licenseFile.set(file('license.txt'))
noticeFile.set(file('notice.txt'))
version = "2.0"
group = 'org.acme'
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package org.elasticsearch.gradle.internal;

import org.apache.commons.io.IOUtils;
import org.elasticsearch.gradle.VersionProperties;
import org.elasticsearch.gradle.internal.test.GradleIntegrationTestCase;
import org.gradle.testkit.runner.BuildResult;
import org.junit.Rule;
Expand Down Expand Up @@ -47,8 +48,9 @@ public void testCheckTask() {
public void testLicenseAndNotice() throws IOException {
BuildResult result = getGradleRunner().withArguments("clean", "assemble").build();
assertTaskSuccessful(result, ":assemble");
assertBuildFileExists(result, projectName(), "distributions/elasticsearch.build.jar");
try (ZipFile zipFile = new ZipFile(new File(getBuildDir(projectName()), "distributions/elasticsearch.build.jar"))) {
String expectedFilePath = "distributions/elasticsearch.build.jar";
assertBuildFileExists(result, projectName(), expectedFilePath);
try (ZipFile zipFile = new ZipFile(new File(getBuildDir(projectName()), expectedFilePath))) {
ZipEntry licenseEntry = zipFile.getEntry("META-INF/LICENSE.txt");
ZipEntry noticeEntry = zipFile.getEntry("META-INF/NOTICE.txt");
assertNotNull("Jar does not have META-INF/LICENSE.txt", licenseEntry);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import java.io.File;
import java.io.IOException;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.modifier.Ownership;
import net.bytebuddy.description.modifier.Visibility;
Expand Down
14 changes: 14 additions & 0 deletions build-tools-internal/src/main/groovy/elasticsearch.base.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import org.elasticsearch.gradle.VersionProperties

project.setDescription("Elasticsearch subproject " + project.getPath());
// common maven publishing configuration
project.setGroup("org.elasticsearch");
project.setVersion(VersionProperties.getElasticsearch())
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,81 @@

package org.elasticsearch.gradle.internal;

import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.elasticsearch.gradle.internal.info.GlobalBuildInfoPlugin;
import org.elasticsearch.gradle.internal.precommit.InternalPrecommitTasks;
import org.elasticsearch.gradle.internal.precommit.JarHellPrecommitPlugin;
import org.elasticsearch.gradle.internal.precommit.SplitPackagesAuditPrecommitPlugin;
import org.gradle.api.GradleException;
import org.gradle.api.InvalidUserDataException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.provider.ProviderFactory;
import org.gradle.api.tasks.bundling.Jar;
import org.gradle.initialization.layout.BuildLayout;

import javax.inject.Inject;
import java.io.File;

/**
* Encapsulates build configuration for elasticsearch projects.
*/
public class BuildPlugin implements Plugin<Project> {

public static final String SSPL_LICENSE_PATH = "licenses/SSPL-1.0+ELASTIC-LICENSE-2.0.txt";

private final BuildLayout buildLayout;
private final ObjectFactory objectFactory;
private final ProviderFactory providerFactory;
private final ProjectLayout projectLayout;

@Inject
BuildPlugin(BuildLayout buildLayout, ObjectFactory objectFactory, ProviderFactory providerFactory, ProjectLayout projectLayout){
this.buildLayout = buildLayout;
this.objectFactory = objectFactory;
this.providerFactory = providerFactory;
this.projectLayout = projectLayout;
}

@Override
public void apply(final Project project) {
// make sure the global build info plugin is applied to the root project
project.getRootProject().getPluginManager().apply(GlobalBuildInfoPlugin.class);

if (project.getPluginManager().hasPlugin("elasticsearch.standalone-rest-test")) {
throw new InvalidUserDataException(
"elasticsearch.standalone-test, " + "elasticsearch.standalone-rest-test, and elasticsearch.build are mutually exclusive"
);
}

project.getPluginManager().apply("elasticsearch.java");
configureLicenseAndNotice(project);
project.getPluginManager().apply("elasticsearch.publish");
project.getPluginManager().apply(DependenciesInfoPlugin.class);
project.getPluginManager().apply(DependenciesGraphPlugin.class);

InternalPrecommitTasks.create(project, true);
configureLicenseAndNotice(project);
}

public static void configureLicenseAndNotice(final Project project) {

public void configureLicenseAndNotice(final Project project) {
final ExtraPropertiesExtension ext = project.getExtensions().getByType(ExtraPropertiesExtension.class);
ext.set("licenseFile", null);
ext.set("noticeFile", null);
// add license/notice files
project.afterEvaluate(p -> p.getTasks().withType(Jar.class).configureEach(jar -> {
if (ext.has("licenseFile") == false
|| ext.get("licenseFile") == null
|| ext.has("noticeFile") == false
|| ext.get("noticeFile") == null) {
throw new GradleException("Must specify license and notice file for project " + p.getPath());
}
final File licenseFile = DefaultGroovyMethods.asType(ext.get("licenseFile"), File.class);
final File noticeFile = DefaultGroovyMethods.asType(ext.get("noticeFile"), File.class);
RegularFileProperty licenseFileProperty = objectFactory.fileProperty();
RegularFileProperty noticeFileProperty = objectFactory.fileProperty();
ext.set("licenseFile", licenseFileProperty);
ext.set("noticeFile", noticeFileProperty);

configureLicenseDefaultConvention(licenseFileProperty);
configureNoticeDefaultConvention(noticeFileProperty);

updateJarTasksMetaInf(project);
}

private void updateJarTasksMetaInf(Project project) {
final ExtraPropertiesExtension ext = project.getExtensions().getByType(ExtraPropertiesExtension.class);
project.getTasks().withType(Jar.class).configureEach(jar -> {
final RegularFileProperty licenseFileExtProperty = (RegularFileProperty) ext.get("licenseFile");
final RegularFileProperty noticeFileExtProperty = (RegularFileProperty) ext.get("noticeFile");
File licenseFile = licenseFileExtProperty.getAsFile().get();
File noticeFile = noticeFileExtProperty.getAsFile().get();
jar.metaInf(spec -> {
spec.from(licenseFile.getParent(), from -> {
from.include(licenseFile.getName());
Expand All @@ -70,6 +93,16 @@ public static void configureLicenseAndNotice(final Project project) {
from.rename(s -> "NOTICE.txt");
});
});
}));
});
}

private void configureLicenseDefaultConvention(RegularFileProperty licenseFileProperty) {
File licenseFileDefault = new File(buildLayout.getRootDirectory(), SSPL_LICENSE_PATH);
licenseFileProperty.convention(projectLayout.file(providerFactory.provider(() -> licenseFileDefault)));
}

private void configureNoticeDefaultConvention(RegularFileProperty noticeFileProperty) {
File noticeFileDefault = new File(buildLayout.getRootDirectory(), "NOTICE.txt");
noticeFileProperty.convention(projectLayout.file(providerFactory.provider(() -> noticeFileDefault)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ plugins {

apply plugin:'elasticsearch.build'

ext.licenseFile = file("LICENSE")
ext.noticeFile = file("NOTICE")
licenseFile.set(file("LICENSE"))
noticeFile.set(file("NOTICE"))

dependencies {
api "junit:junit:${versions.junit}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ allprojects {
repositories {
mavenCentral()
}

dependencies {
testImplementation "junit:junit:4.12"
}

ext.licenseFile = file("$buildDir/dummy/license")
ext.noticeFile = file("$buildDir/dummy/notice")

testingConventions.naming {
// Reset default to no baseClass checks
Tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
package org.elasticsearch.gradle.plugin;

import org.gradle.api.Project;
import org.gradle.api.file.RegularFile;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.plugins.ExtraPropertiesExtension;

import java.io.File;
import java.util.ArrayList;
Expand Down Expand Up @@ -138,7 +141,11 @@ public File getLicenseFile() {
}

public void setLicenseFile(File licenseFile) {
this.project.getExtensions().getExtraProperties().set("licenseFile", licenseFile);
ExtraPropertiesExtension extraProperties = this.project.getExtensions().getExtraProperties();
RegularFileProperty regularFileProperty = extraProperties.has("licenseFile") ?
(RegularFileProperty)extraProperties.get("licenseFile") :
project.getObjects().fileProperty();
regularFileProperty.set(licenseFile);
this.licenseFile = licenseFile;
}

Expand All @@ -147,7 +154,11 @@ public File getNoticeFile() {
}

public void setNoticeFile(File noticeFile) {
this.project.getExtensions().getExtraProperties().set("noticeFile", noticeFile);
ExtraPropertiesExtension extraProperties = this.project.getExtensions().getExtraProperties();
RegularFileProperty regularFileProperty = extraProperties.has("noticeFile") ?
(RegularFileProperty)extraProperties.get("noticeFile") :
project.getObjects().fileProperty();
regularFileProperty.set(noticeFile);
this.noticeFile = noticeFile;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ protected GradleRunner getGradleRunner() {
.withProjectDir(getProjectDir())
.withPluginClasspath()
.withTestKitDir(testkit)
.forwardOutput()
.withDebug(ManagementFactory.getRuntimeMXBean().getInputArguments().toString().indexOf("-agentlib:jdwp") > 0));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,11 @@

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Collectors;

import static org.junit.Assert.fail;

public class TestUtils {

public static void setupJarJdkClasspath(File projectRoot) {
try {
URL originLocation = TestUtils.class.getClassLoader()
.loadClass("org.elasticsearch.jdk.JdkJarHellCheck")
.getProtectionDomain()
.getCodeSource()
.getLocation();
File targetFile = new File(
projectRoot,
"sample_jars/build/testrepo/org/elasticsearch/elasticsearch-core/current/elasticsearch-core-current.jar"
);
targetFile.getParentFile().mkdirs();
Path originalPath = Paths.get(originLocation.toURI());
Files.copy(originalPath, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (ClassNotFoundException | URISyntaxException | IOException e) {
e.printStackTrace();
fail("Cannot setup jdk jar hell classpath");
}
}

public static String normalizeString(String input, File projectRootDir) {
try {
String normalizedPathPrefix = projectRootDir.getCanonicalPath().replaceAll("\\\\", "/");
Expand Down
16 changes: 1 addition & 15 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ plugins {
id "com.diffplug.spotless" version "5.15.1" apply false
}

String licenseCommit
if (VersionProperties.elasticsearch.toString().endsWith('-SNAPSHOT')) {
licenseCommit = BuildParams.gitRevision ?: "master" // leniency for non git builds
} else {
licenseCommit = "v${version}"
}

/**
* This is a convenient method for declaring test artifact dependencies provided by the internal
* test artifact plugin. It replaces basically the longer dependency notation with explicit capability
Expand Down Expand Up @@ -168,20 +161,13 @@ if (project.gradle.startParameter.taskNames.find { it.startsWith("checkPart") }
}

subprojects {
// common maven publishing configuration
group = 'org.elasticsearch'
version = VersionProperties.elasticsearch
description = "Elasticsearch subproject ${project.path}"
apply plugin: 'elasticsearch.base'
}

allprojects {
// We disable this plugin for now till we shaked out the issues we see
// e.g. see https://github.com/elastic/elasticsearch/issues/72169
// apply plugin:'elasticsearch.internal-test-rerun'
plugins.withType(BuildPlugin).whenPluginAdded {
project.licenseFile = project.rootProject.file('licenses/SSPL-1.0+ELASTIC-LICENSE-2.0.txt')
project.noticeFile = project.rootProject.file('NOTICE.txt')
}

plugins.withType(InternalPluginBuildPlugin).whenPluginAdded {
project.dependencies {
Expand Down
Loading

0 comments on commit dd4e4c3

Please sign in to comment.