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

[7.x] Include vendored code notices in distribution notice files #57569

Merged
merged 2 commits into from
Jun 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 99 additions & 18 deletions buildSrc/src/main/groovy/org/elasticsearch/gradle/NoticeTask.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* 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
* 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
Expand All @@ -20,27 +20,32 @@
package org.elasticsearch.gradle

import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

/**
* A task to create a notice file which includes dependencies' notices.
*/
public class NoticeTask extends DefaultTask {
class NoticeTask extends DefaultTask {

@InputFile
File inputFile = project.rootProject.file('NOTICE.txt')

@OutputFile
File outputFile = new File(project.buildDir, "notices/${name}/NOTICE.txt")

private FileTree sources

/** Directories to include notices from */
private List<File> licensesDirs = new ArrayList<>()

public NoticeTask() {
NoticeTask() {
description = 'Create a notice file from dependencies'
// Default licenses directory is ${projectDir}/licenses (if it exists)
File licensesDir = new File(project.projectDir, 'licenses')
Expand All @@ -50,46 +55,122 @@ public class NoticeTask extends DefaultTask {
}

/** Add notices from the specified directory. */
public void licensesDir(File licensesDir) {
void licensesDir(File licensesDir) {
licensesDirs.add(licensesDir)
}

void source(Object source) {
if (sources == null) {
sources = project.fileTree(source)
} else {
sources += project.fileTree(source)
}
}

void source(SourceDirectorySet source) {
if (sources == null) {
sources = source
} else {
sources += source
}
}

@TaskAction
public void generateNotice() {
void generateNotice() {
StringBuilder output = new StringBuilder()
output.append(inputFile.getText('UTF-8'))
output.append('\n\n')
// This is a map rather than a set so that the sort order is the 3rd
// party component names, unaffected by the full path to the various files
Map<String, File> seen = new TreeMap<>()
for (File licensesDir : licensesDirs) {
licensesDir.eachFileMatch({ it ==~ /.*-NOTICE\.txt/ }) { File file ->
String name = file.name.substring(0, file.name.length() - '-NOTICE.txt'.length())
if (seen.containsKey(name)) {
File prevFile = seen.get(name)
if (prevFile.text != file.text) {
throw new RuntimeException("Two different notices exist for dependency '" +
name + "': " + prevFile + " and " + file)
}
} else {
seen.put(name, file)
noticeFiles.each { File file ->
String name = file.name.replaceFirst(/-NOTICE\.txt$/, "")
if (seen.containsKey(name)) {
File prevFile = seen.get(name)
if (prevFile.text != file.text) {
throw new RuntimeException("Two different notices exist for dependency '" +
name + "': " + prevFile + " and " + file)
}
} else {
seen.put(name, file)
}
}

// Add all LICENSE and NOTICE files in licenses directory
for (Map.Entry<String, File> entry : seen.entrySet()) {
String name = entry.getKey()
File file = entry.getValue()
appendFile(file, name, 'NOTICE', output)
appendFile(new File(file.parentFile, "${name}-LICENSE.txt"), name, 'LICENSE', output)
}

// Find any source files with "@notice" annotated license header
for (File sourceFile : sources.files) {
boolean isPackageInfo = sourceFile.name == 'package-info.java'
boolean foundNotice = false
boolean inNotice = false
StringBuilder header = new StringBuilder()
String packageDeclaration

for (String line : sourceFile.readLines()) {
if (isPackageInfo && packageDeclaration == null && line.startsWith('package')) {
packageDeclaration = line
}

if (foundNotice == false) {
foundNotice = line.contains('@notice')
inNotice = true
} else {
if (line.contains('*/')) {
inNotice = false

if (!isPackageInfo) {
break
}
} else if (inNotice) {
header.append(line.stripMargin('*'))
header.append('\n')
}
}
}

if (foundNotice) {
appendText(header.toString(), isPackageInfo ? packageDeclaration : sourceFile.name, '', output)
}
}
outputFile.setText(output.toString(), 'UTF-8')
}

@InputFiles
@Optional
FileCollection getNoticeFiles() {
FileTree tree
licensesDirs.each { dir ->
if (tree == null) {
tree = project.fileTree(dir)
} else {
tree += project.fileTree(dir)
}
}

return tree?.matching { include '**/*-NOTICE.txt' }
}

@InputFiles
@Optional
FileCollection getSources() {
return sources
}

static void appendFile(File file, String name, String type, StringBuilder output) {
String text = file.getText('UTF-8')
if (text.trim().isEmpty()) {
return
}
appendText(text, name, type, output)
}

static void appendText(String text, String name, String type, StringBuilder output) {
output.append('================================================================================\n')
output.append("${name} ${type}\n")
output.append('================================================================================\n')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import org.elasticsearch.gradle.test.rest.RestResourcesPlugin
import org.elasticsearch.gradle.test.RestIntegTestTask
import org.elasticsearch.gradle.testclusters.RunTask
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
import org.elasticsearch.gradle.util.Util
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Plugin
import org.gradle.api.Project
Expand Down Expand Up @@ -256,6 +257,7 @@ class PluginBuildPlugin implements Plugin<Project> {
if (noticeFile != null) {
TaskProvider<NoticeTask> generateNotice = project.tasks.register('generateNotice', NoticeTask) {
inputFile = noticeFile
source(Util.getJavaMainSourceSet(project).get().allJava)
}
project.tasks.named('bundlePlugin').configure {
from(generateNotice)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.SkipWhenEmpty
import org.gradle.api.tasks.SourceSet

import java.nio.file.Files

Expand All @@ -43,7 +42,7 @@ public class LicenseHeadersTask extends AntTask {

/** Allowed license families for this project. */
@Input
List<String> approvedLicenses = ['Apache', 'Generated']
List<String> approvedLicenses = ['Apache', 'Generated', 'Vendored']

/**
* Files that should be excluded from the license header check. Use with extreme care, only in situations where the license on the
Expand All @@ -69,7 +68,7 @@ public class LicenseHeadersTask extends AntTask {
*/
@InputFiles
@SkipWhenEmpty
public List<FileCollection> getJavaFiles() {
List<FileCollection> getJavaFiles() {
return project.sourceSets.collect({it.allJava})
}

Expand All @@ -82,7 +81,7 @@ public class LicenseHeadersTask extends AntTask {
* @param familyName An expanded string name for the license
* @param pattern A pattern to search for, which if found, indicates a file contains the license
*/
public void additionalLicense(String categoryName, String familyName, String pattern) {
void additionalLicense(String categoryName, String familyName, String pattern) {
if (categoryName.length() != 5) {
throw new IllegalArgumentException("License category name must be exactly 5 characters, got ${categoryName}");
}
Expand Down Expand Up @@ -120,10 +119,6 @@ public class LicenseHeadersTask extends AntTask {
licenseFamilyName: "Apache") {
// Apache license (ES)
pattern(substring: "Licensed to Elasticsearch under one or more contributor")
// Apache license (ASF)
pattern(substring: "Licensed to the Apache Software Foundation (ASF) under")
// this is the old-school one under some files
pattern(substring: "Licensed under the Apache License, Version 2.0 (the \"License\")")
}

// Generated resources
Expand All @@ -133,6 +128,12 @@ public class LicenseHeadersTask extends AntTask {
pattern(substring: "ANTLR GENERATED CODE")
}

// Vendored Code
substringMatcher(licenseFamilyCategory: "VEN ",
licenseFamilyName: "Vendored") {
pattern(substring: "@notice")
}

// license types added by the project
for (Map.Entry<String, String[]> additional : additionalLicenses.entrySet()) {
String category = additional.getKey().substring(0, 5)
Expand Down
28 changes: 18 additions & 10 deletions distribution/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,29 @@ tasks.register("generateDependenciesReport", ConcatFilesTask) {
*****************************************************************************/

// integ test zip only uses server, so a different notice file is needed there
task buildServerNotice(type: NoticeTask) {
licensesDir new File(project(':server').projectDir, 'licenses')
}
task buildServerNotice(type: NoticeTask)

// other distributions include notices from modules as well, which are added below later
task buildDefaultNotice(type: NoticeTask) {
licensesDir new File(project(':server').projectDir, 'licenses')
licensesDir new File(project(':distribution').projectDir, 'licenses')
}

task buildOssNotice(type: NoticeTask) {
licensesDir new File(project(':server').projectDir, 'licenses')
licensesDir new File(project(':distribution').projectDir, 'licenses')
}
task buildDefaultNoJdkNotice(type: NoticeTask) {
licensesDir new File(project(':server').projectDir, 'licenses')
}
task buildOssNoJdkNotice(type: NoticeTask) {
licensesDir new File(project(':server').projectDir, 'licenses')

task buildDefaultNoJdkNotice(type: NoticeTask)

task buildOssNoJdkNotice(type: NoticeTask)

// The :server and :libs projects belong to all distributions
tasks.withType(NoticeTask) {
licensesDir project(':server').file('licenses')
source project(':server').file('src/main/java')
project(':libs').subprojects.each { Project lib ->
licensesDir lib.file('licenses')
source lib.file('src/main/java')
}
}

/*****************************************************************************
Expand Down Expand Up @@ -211,7 +216,9 @@ project.rootProject.subprojects.findAll { it.parent.path == ':modules' }.each {
File licenses = new File(module.projectDir, 'licenses')
if (licenses.exists()) {
buildDefaultNotice.licensesDir licenses
buildDefaultNotice.source module.file('src/main/java')
buildOssNotice.licensesDir licenses
buildOssNotice.source module.file('src/main/java')
}

copyModule(processOssOutputs, module)
Expand All @@ -235,6 +242,7 @@ xpack.subprojects.findAll { it.parent == xpack }.each { Project xpackModule ->
File licenses = new File(xpackModule.projectDir, 'licenses')
if (licenses.exists()) {
buildDefaultNotice.licensesDir licenses
buildDefaultNotice.source xpackModule.file('src/main/java')
}
copyModule(processDefaultOutputs, xpackModule)
copyLog4jProperties(buildDefaultLog4jConfig, xpackModule)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/* @notice
* Copyright 2012 Jeff Hain
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -12,9 +12,7 @@
* 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.
*/

/*
*
* =============================================================================
* Notice of fdlibm package this program is partially derived from:
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/* @notice
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
Expand All @@ -13,6 +13,8 @@
* 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.
*
* Modifications copyright (C) 2020 Elasticsearch B.V.
*/

package org.elasticsearch.core.internal.io;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/* @notice
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/* @notice
Copyright (c) 1998-2010 AOL Inc.

Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.elasticsearch.index.analysis;

/*
/* @notice
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.elasticsearch.index.analysis;

/*
/* @notice
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.elasticsearch.index.analysis;

/*
/* @notice
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
Expand Down
4 changes: 4 additions & 0 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ dependencyLicenses {
}
}

licenseHeaders {
// Ignore our vendored version of Google Guice
excludes << 'org/elasticsearch/common/inject/**/*'
}

tasks.named('internalClusterTest').configure {
if (org.elasticsearch.gradle.info.BuildParams.isSnapshotBuild() == false) {
Expand Down
Loading