You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/11/03 20:59:59 UTC

[3/3] lucene-solr:jira/gradle: Add checkLicenses task

Add checkLicenses task


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/d541e180
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d541e180
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d541e180

Branch: refs/heads/jira/gradle
Commit: d541e180047cfc2a0c528caa7216fcb3f34678bb
Parents: 15f3bce
Author: Cao Manh Dat <da...@apache.org>
Authored: Sat Nov 3 20:59:48 2018 +0000
Committer: Cao Manh Dat <da...@apache.org>
Committed: Sat Nov 3 20:59:48 2018 +0000

----------------------------------------------------------------------
 build.gradle                                    |   9 +-
 buildSrc/build.gradle                           |  14 +
 .../lucene/gradle/CheckWorkingCopy.groovy       |  81 +++++
 .../apache/lucene/gradle/LicenseCheckTask.java  | 300 ++++++++++++++++
 .../apache/lucene/gradle/LicenseCheckTask2.java | 356 +++++++++++++++++++
 .../org/apache/lucene/gradle/LicenseType.java   |  75 ++++
 lucene/build.gradle                             |   8 +
 lucene/licenses/asm-tree-5.1.jar.sha1           |   1 +
 lucene/licenses/commons-codec-1.9.jar.sha1      |   1 +
 lucene/licenses/commons-logging-1.1.3.jar.sha1  |   1 -
 lucene/licenses/commons-logging-1.2.jar.sha1    |   1 +
 lucene/licenses/hamcrest-core-1.1.jar.sha1      |   1 +
 lucene/licenses/hamcrest-core-LICENSE-BSD.txt   |  27 ++
 lucene/licenses/hamcrest-core-NOTICE.txt        |   0
 .../jetty-security-9.4.11.v20180605.jar.sha1    |   1 +
 lucene/replicator/ivy.xml                       |   2 -
 solr/build.gradle                               |   8 +
 17 files changed, 880 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/build.gradle
----------------------------------------------------------------------
diff --git a/build.gradle b/build.gradle
index 6013e5f..6c46251 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,3 +1,5 @@
+import org.apache.lucene.gradle.CheckWorkingCopy
+
 /*
  * This file was generated by the Gradle 'init' task.
  *
@@ -31,11 +33,12 @@ subprojects {
 		}
 	}
 	tasks.withType(Test) {
-	        maxParallelForks = Runtime.runtime.availableProcessors() / 2
-    	}
-
+		maxParallelForks = Runtime.runtime.availableProcessors() / 2
+	}
 }
 
+tasks.create('checkWorkingCopy', CheckWorkingCopy, true)
+
 // These versions are defined here because they represent
 // a dependency version which should match across multiple
 // Maven artifacts.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/buildSrc/build.gradle
----------------------------------------------------------------------
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
new file mode 100644
index 0000000..20921a9
--- /dev/null
+++ b/buildSrc/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'groovy'
+apply plugin: 'java'
+group = 'org.lucene.gradle'
+
+repositories {
+	jcenter()
+}
+
+dependencies {
+	compile gradleApi()
+	compile localGroovy()
+
+	compile 'org.eclipse.jgit:org.eclipse.jgit:4.6.0.201612231935-r'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/buildSrc/src/main/groovy/org/apache/lucene/gradle/CheckWorkingCopy.groovy
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/groovy/org/apache/lucene/gradle/CheckWorkingCopy.groovy b/buildSrc/src/main/groovy/org/apache/lucene/gradle/CheckWorkingCopy.groovy
new file mode 100644
index 0000000..8680f11
--- /dev/null
+++ b/buildSrc/src/main/groovy/org/apache/lucene/gradle/CheckWorkingCopy.groovy
@@ -0,0 +1,81 @@
+package org.apache.lucene.gradle
+/*
+ * 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.
+ * The ASF licenses this file to You 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.
+ */
+
+/** Task script that is called by Ant's build.xml file:
+ * Checks GIT working copy for unversioned or modified files.
+ */
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.Status;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
+import org.eclipse.jgit.errors.*;
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.tasks.TaskAction
+
+import javax.inject.Inject
+
+class CheckWorkingCopy extends DefaultTask {
+  boolean failonmodifications
+
+  @Inject
+  CheckWorkingCopy(boolean failonmodifications) {
+    this.failonmodifications = failonmodifications
+  }
+
+  @TaskAction
+  void check() {
+    try {
+      println 'Initializing working copy...' + failonmodifications
+      final Repository repository = new FileRepositoryBuilder()
+        .setWorkTree(project.getRootDir())
+        .setMustExist(true)
+        .build();
+
+      println 'Checking working copy status...'
+      final Status status = new Git(repository).status().call();
+      if (!status.isClean()) {
+        final SortedSet unversioned = new TreeSet(), modified = new TreeSet();
+        status.properties.each{ prop, val ->
+          if (val instanceof Set) {
+            if (prop in ['untracked', 'untrackedFolders', 'missing']) {
+              unversioned.addAll(val);
+            } else if (prop != 'ignoredNotInIndex') {
+              modified.addAll(val);
+            }
+          }
+        }
+        if (unversioned) {
+          throw new GradleException(
+                  "Source checkout is dirty (unversioned/missing files) after running tests!!! Offending files:\n" +
+                  '* ' + unversioned.join('\n* '))
+        }
+        if (failonmodifications) {
+          throw new GradleException(
+                  "Source checkout is modified!!! Offending files:\n"+
+                          '* ' + unversioned.join('\n* '))
+        }
+      }
+    } catch (RepositoryNotFoundException | NoWorkTreeException | NotSupportedException e) {
+      logger.error('WARNING: Development directory is not a valid GIT checkout! Disabling checks...')
+    }
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask.java
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask.java b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask.java
new file mode 100644
index 0000000..271d1b8
--- /dev/null
+++ b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask.java
@@ -0,0 +1,300 @@
+package org.apache.lucene.gradle;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.GradleException;
+import org.gradle.api.artifacts.ResolvedArtifact;
+import org.gradle.api.logging.LogLevel;
+
+import javax.inject.Inject;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+public class LicenseCheckTask extends DefaultTask {
+  private final static String CHECKSUM_TYPE = "sha1";
+  private static final int CHECKSUM_BUFFER_SIZE = 8 * 1024;
+  private static final int CHECKSUM_BYTE_MASK = 0xFF;
+  private static final String FAILURE_MESSAGE = "License check failed. Check the logs.\n"
+      + "If you recently modified ivy-versions.properties or any module's build.gradle,\n"
+      + "make sure you run \"ant clean-jars jar-checksums\" before running precommit.";
+  private static final Map<Pattern, String> REPLACE_PATTERNS = new HashMap<>();
+  static {
+    REPLACE_PATTERNS.put(Pattern.compile("jetty([^/]+)$"), "jetty");
+    REPLACE_PATTERNS.put(Pattern.compile("asm([^/]+)$"), "asm");
+    REPLACE_PATTERNS.put(Pattern.compile("slf4j-([^/]+)$"), "slf4j");
+    REPLACE_PATTERNS.put(Pattern.compile("javax\\.servlet([^/]+)$"), "javax.servlet");
+    REPLACE_PATTERNS.put(Pattern.compile("(bcmail|bcprov)-([^/]+)$"), "\1");
+  }
+
+  private Pattern skipRegexChecksum;
+  private boolean skipSnapshotsChecksum;
+  private boolean skipChecksum;
+
+
+  /**
+   * Directory containing licenses
+   */
+  private File licenseDirectory;
+
+  /**
+   * Failure flag.
+   */
+  private boolean failures;
+
+  @Inject
+  public LicenseCheckTask(File licenseDirectory) {
+    this.licenseDirectory = licenseDirectory;
+    doLast(task -> {
+      Set<ResolvedArtifact> deps = new HashSet<>();
+      getProject().getSubprojects().forEach(project -> {
+        project.getConfigurations().forEach(config -> {
+          if (config.isCanBeResolved()) {
+            deps.addAll(config.getResolvedConfiguration().getResolvedArtifacts());
+          }
+        });
+      });
+      deps.removeIf(artifact -> {
+        String groupName = artifact.getModuleVersion().getId().getGroup();
+        return "org.apache.lucene".equals(groupName) || "org.apache.solr".equals(groupName);
+      });
+
+      doCheck(deps);
+    });
+  }
+
+  public void setSkipSnapshotsChecksum(boolean skipSnapshotsChecksum) {
+    this.skipSnapshotsChecksum = skipSnapshotsChecksum;
+  }
+
+  public void setSkipChecksum(boolean skipChecksum) {
+    this.skipChecksum = skipChecksum;
+  }
+
+  public void setSkipRegexChecksum(String skipRegexChecksum) {
+    try {
+      if (skipRegexChecksum != null && skipRegexChecksum.length() > 0) {
+        this.skipRegexChecksum = Pattern.compile(skipRegexChecksum);
+      }
+    } catch (PatternSyntaxException e) {
+      throw new GradleException("Unable to compile skipRegexChecksum pattern.  Reason: "
+          + e.getMessage() + " " + skipRegexChecksum, e);
+    }
+  }
+
+  /**
+   * Execute the task.
+   */
+  public void doCheck(Set<ResolvedArtifact> jars) {
+    if (skipChecksum) {
+      getLogger().info("Skipping checksum verification for dependencies");
+    } else {
+      if (skipSnapshotsChecksum) {
+        getLogger().info("Skipping checksum for SNAPSHOT dependencies");
+      }
+
+      if (skipRegexChecksum != null) {
+        getLogger().info("Skipping checksum for dependencies matching regex: " + skipRegexChecksum.pattern());
+      }
+    }
+
+    processJars(jars);
+
+    if (failures) {
+      throw new GradleException(FAILURE_MESSAGE);
+    }
+  }
+
+  /**
+   * Process all JARs.
+   */
+  private void processJars(Set<ResolvedArtifact> jars) {
+    long start = System.currentTimeMillis();
+    int errors = 0;
+    int checked = 0;
+    for (ResolvedArtifact artifact : jars) {
+      if (!checkJarFile(artifact)) {
+        errors++;
+      }
+      checked++;
+    }
+
+    getLogger().log(errors > 0 ? LogLevel.ERROR : LogLevel.INFO, String.format(Locale.ROOT,
+        "Scanned %d JAR file(s) for licenses (in %.2fs.), %d error(s).",
+        checked, (System.currentTimeMillis() - start) / 1000.0, errors));
+  }
+
+  /**
+   * Check a single JAR file.
+   */
+  private boolean checkJarFile(ResolvedArtifact artifact) {
+    File jarFile = artifact.getFile();
+    getLogger().debug("Scanning: {}", jarFile.getPath());
+
+    if (!skipChecksum) {
+      boolean skipDueToSnapshot = skipSnapshotsChecksum && jarFile.getName().contains("-SNAPSHOT");
+      if (!skipDueToSnapshot && !matchesRegexChecksum(jarFile, skipRegexChecksum)) {
+        // validate the jar matches against our expected hash
+        final File checksumFile = new File(licenseDirectory, jarFile.getName()
+            + "." + CHECKSUM_TYPE);
+        if (!(checksumFile.exists() && checksumFile.canRead())) {
+          getLogger().error("MISSING {} checksum file for: {}", CHECKSUM_TYPE, jarFile.getPath());
+          this.failures = true;
+          return false;
+        } else {
+          final String expectedChecksum = readChecksumFile(checksumFile);
+          try {
+            final MessageDigest md = MessageDigest.getInstance(CHECKSUM_TYPE);
+            byte[] buf = new byte[CHECKSUM_BUFFER_SIZE];
+            try {
+              FileInputStream fis = new FileInputStream(jarFile);
+              try {
+                DigestInputStream dis = new DigestInputStream(fis, md);
+                try {
+                  while (dis.read(buf, 0, CHECKSUM_BUFFER_SIZE) != -1) {
+                    // NOOP
+                  }
+                } finally {
+                  dis.close();
+                }
+              } finally {
+                fis.close();
+              }
+            } catch (IOException ioe) {
+              throw new GradleException("IO error computing checksum of file: "
+                  + jarFile, ioe);
+            }
+            final byte[] checksumBytes = md.digest();
+            final String checksum = createChecksumString(checksumBytes);
+            if (!checksum.equals(expectedChecksum)) {
+              getLogger().error("CHECKSUM FAILED for " + jarFile.getPath() + " (expected: \""
+                  + expectedChecksum + "\" was: \"" + checksum + "\")");
+              this.failures = true;
+              return false;
+            }
+
+          } catch (NoSuchAlgorithmException ae) {
+            throw new GradleException("Digest type " + CHECKSUM_TYPE
+                + " not supported by your JVM", ae);
+          }
+        }
+      } else if (skipDueToSnapshot) {
+        getLogger().info("Skipping jar because it is a SNAPSHOT : "
+            + jarFile.getAbsolutePath());
+      } else {
+        getLogger().info("Skipping jar because it matches regex pattern: {} pattern:{}",
+            jarFile.getAbsolutePath(), skipRegexChecksum.pattern());
+      }
+    }
+
+    // Get the expected license path base from the mapper and search for license files.
+    Map<File, LicenseType> foundLicenses = new LinkedHashMap<>();
+    List<File> expectedLocations = new ArrayList<>();
+    outer:
+    for (LicenseType licenseType : LicenseType.values()) {
+      String artifactName = artifact.getModuleVersion().getId().getName();
+      for (Map.Entry<Pattern, String> entry : REPLACE_PATTERNS.entrySet()) {
+        artifactName = entry.getKey().matcher(artifactName).replaceAll(entry.getValue());
+      }
+      File licensePath = new File(licenseDirectory,
+          artifactName + licenseType.licenseFileSuffix());
+      if (licensePath.exists()) {
+        foundLicenses.put(licensePath, licenseType);
+        getLogger().debug(" FOUND " + licenseType.name() + " license at " + licensePath.getPath());
+        // We could continue scanning here to detect duplicate associations?
+        break outer;
+      } else {
+        expectedLocations.add(licensePath);
+      }
+    }
+
+    // Check for NOTICE files.
+    for (Map.Entry<File, LicenseType> e : foundLicenses.entrySet()) {
+      LicenseType license = e.getValue();
+      String licensePath = e.getKey().getName();
+      String baseName = licensePath.substring(
+          0, licensePath.length() - license.licenseFileSuffix().length());
+      File noticeFile = new File(licenseDirectory, baseName + license.noticeFileSuffix());
+
+      if (noticeFile.exists()) {
+        getLogger().debug(" FOUND NOTICE file at " + noticeFile.getAbsolutePath());
+      } else {
+        if (license.isNoticeRequired()) {
+          this.failures = true;
+          getLogger().error("MISSING NOTICE for the license file:\n  "
+              + licensePath + "\n  Expected location below:\n  "
+              + noticeFile.getAbsolutePath());
+        }
+      }
+    }
+
+    // In case there is something missing, complain.
+    if (foundLicenses.isEmpty()) {
+      this.failures = true;
+      StringBuilder message = new StringBuilder();
+      message.append(
+          "MISSING LICENSE for the following file:\n  " + jarFile.getAbsolutePath()
+              + "\n  Expected locations below:\n");
+      for (File location : expectedLocations) {
+        message.append("  => ").append(location.getAbsolutePath()).append("\n");
+      }
+      getLogger().error(message.toString());
+      return false;
+    }
+
+    return true;
+  }
+
+  private static String createChecksumString(byte[] digest) {
+    StringBuilder checksum = new StringBuilder();
+    for (byte aDigest : digest) {
+      checksum.append(String.format(Locale.ROOT, "%02x",
+          CHECKSUM_BYTE_MASK & aDigest));
+    }
+    return checksum.toString();
+  }
+
+  private static String readChecksumFile(File f) {
+    BufferedReader reader = null;
+    try {
+      reader = new BufferedReader(new InputStreamReader
+          (new FileInputStream(f), StandardCharsets.UTF_8));
+      try {
+        String checksum = reader.readLine();
+        if (null == checksum || 0 == checksum.length()) {
+          throw new GradleException("Failed to find checksum in file: " + f);
+        }
+        return checksum;
+      } finally {
+        reader.close();
+      }
+    } catch (IOException e) {
+      throw new GradleException("IO error reading checksum file: " + f, e);
+    }
+  }
+
+  private static final boolean matchesRegexChecksum(File jarFile, Pattern skipRegexChecksum) {
+    if (skipRegexChecksum == null) {
+      return false;
+    }
+    Matcher m = skipRegexChecksum.matcher(jarFile.getName());
+    return m.matches();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask2.java
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask2.java b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask2.java
new file mode 100644
index 0000000..83a93b6
--- /dev/null
+++ b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseCheckTask2.java
@@ -0,0 +1,356 @@
+///*
+// * 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.
+// * The ASF licenses this file to You 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 org.apache.lucene.gradle;
+//
+//import java.io.File;
+//import java.io.FileInputStream;
+//import java.io.BufferedReader;
+//import java.io.InputStreamReader;
+//import java.io.IOException;
+//import java.nio.charset.StandardCharsets;
+//import java.util.ArrayList;
+//import java.util.Iterator;
+//import java.util.LinkedHashMap;
+//import java.util.List;
+//import java.util.Locale;
+//import java.util.Map;
+//import java.util.regex.Matcher;
+//import java.util.regex.Pattern;
+//import java.util.regex.PatternSyntaxException;
+//import java.security.DigestInputStream;
+//import java.security.MessageDigest;
+//import java.security.NoSuchAlgorithmException;
+//
+//import org.apache.tools.ant.BuildException;
+//import org.apache.tools.ant.Project;
+//import org.apache.tools.ant.Task;
+//import org.apache.tools.ant.types.Mapper;
+//import org.apache.tools.ant.types.Resource;
+//import org.apache.tools.ant.types.ResourceCollection;
+//import org.apache.tools.ant.types.resources.FileResource;
+//import org.apache.tools.ant.types.resources.Resources;
+//import org.apache.tools.ant.util.FileNameMapper;
+//import org.gradle.api.DefaultTask;
+//
+///**
+// * An ANT task that verifies if JAR file have associated <tt>LICENSE</tt>,
+// * <tt>NOTICE</tt>, and <tt>sha1</tt> files.
+// */
+//public class LicenseCheckTask extends DefaultTask {
+//
+//    public final static String CHECKSUM_TYPE = "sha1";
+//    private static final int CHECKSUM_BUFFER_SIZE = 8 * 1024;
+//    private static final int CHECKSUM_BYTE_MASK = 0xFF;
+//    private static final String FAILURE_MESSAGE = "License check failed. Check the logs.\n"
+//            + "If you recently modified ivy-versions.properties or any module's ivy.xml,\n"
+//            + "make sure you run \"ant clean-jars jar-checksums\" before running precommit.";
+//
+//    private Pattern skipRegexChecksum;
+//    private boolean skipSnapshotsChecksum;
+//    private boolean skipChecksum;
+//
+//    /**
+//     * All JAR files to check.
+//     */
+//    private Resources jarResources = new Resources();
+//
+//    /**
+//     * Directory containing licenses
+//     */
+//    private File licenseDirectory;
+//
+//    /**
+//     * License file mapper.
+//     */
+//    private FileNameMapper licenseMapper;
+//
+//    /**
+//     * A logging level associated with verbose logging.
+//     */
+//    private int verboseLevel = Project.MSG_VERBOSE;
+//
+//    /**
+//     * Failure flag.
+//     */
+//    private boolean failures;
+//
+//    /**
+//     * Adds a set of JAR resources to check.
+//     */
+//    public void add(ResourceCollection rc) {
+//        jarResources.add(rc);
+//    }
+//
+//    /**
+//     * Adds a license mapper.
+//     */
+//    public void addConfiguredLicenseMapper(Mapper mapper) {
+//        if (licenseMapper != null) {
+//            throw new BuildException("Only one license mapper is allowed.");
+//        }
+//        this.licenseMapper = mapper.getImplementation();
+//    }
+//
+//    public void setVerbose(boolean verbose) {
+//        verboseLevel = (verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
+//    }
+//
+//    public void setLicenseDirectory(File file) {
+//        licenseDirectory = file;
+//    }
+//
+//    public void setSkipSnapshotsChecksum(boolean skipSnapshotsChecksum) {
+//        this.skipSnapshotsChecksum = skipSnapshotsChecksum;
+//    }
+//
+//    public void setSkipChecksum(boolean skipChecksum) {
+//        this.skipChecksum = skipChecksum;
+//    }
+//
+//    public void setSkipRegexChecksum(String skipRegexChecksum) {
+//        try {
+//            if (skipRegexChecksum != null && skipRegexChecksum.length() > 0) {
+//                this.skipRegexChecksum = Pattern.compile(skipRegexChecksum);
+//            }
+//        } catch (PatternSyntaxException e) {
+//            throw new BuildException("Unable to compile skipRegexChecksum pattern.  Reason: "
+//                    + e.getMessage() + " " + skipRegexChecksum, e);
+//        }
+//    }
+//
+//    /**
+//     * Execute the task.
+//     */
+//
+//    public void execute() throws BuildException {
+//        this.getTaskDependencies().visitDependencies();
+//        if (licenseMapper == null) {
+//            throw new BuildException("Expected an embedded <licenseMapper>.");
+//        }
+//
+//        if (skipChecksum) {
+//            log("Skipping checksum verification for dependencies", Project.MSG_INFO);
+//        } else {
+//            if (skipSnapshotsChecksum) {
+//                log("Skipping checksum for SNAPSHOT dependencies", Project.MSG_INFO);
+//            }
+//
+//            if (skipRegexChecksum != null) {
+//                log("Skipping checksum for dependencies matching regex: " + skipRegexChecksum.pattern(),
+//                        Project.MSG_INFO);
+//            }
+//        }
+//
+//        jarResources.setProject(getProject());
+//        processJars();
+//
+//        if (failures) {
+//            throw new BuildException(FAILURE_MESSAGE);
+//        }
+//    }
+//
+//    /**
+//     * Process all JARs.
+//     */
+//    private void processJars() {
+//        log("Starting scan.", verboseLevel);
+//        long start = System.currentTimeMillis();
+//
+//        @SuppressWarnings("unchecked")
+//        Iterator<Resource> iter = (Iterator<Resource>) jarResources.iterator();
+//        int checked = 0;
+//        int errors = 0;
+//        while (iter.hasNext()) {
+//            final Resource r = iter.next();
+//            if (!r.isExists()) {
+//                throw new BuildException("JAR resource does not exist: " + r.getName());
+//            }
+//            if (!(r instanceof FileResource)) {
+//                throw new BuildException("Only filesystem resource are supported: " + r.getName()
+//                        + ", was: " + r.getClass().getName());
+//            }
+//
+//            File jarFile = ((FileResource) r).getFile();
+//            if (! checkJarFile(jarFile) ) {
+//                errors++;
+//            }
+//            checked++;
+//        }
+//
+//        log(String.format(Locale.ROOT,
+//                "Scanned %d JAR file(s) for licenses (in %.2fs.), %d error(s).",
+//                checked, (System.currentTimeMillis() - start) / 1000.0, errors),
+//                errors > 0 ? Project.MSG_ERR : Project.MSG_INFO);
+//    }
+//
+//    /**
+//     * Check a single JAR file.
+//     */
+//    private boolean checkJarFile(File jarFile) {
+//        log("Scanning: " + jarFile.getPath(), verboseLevel);
+//
+//        if (!skipChecksum) {
+//            boolean skipDueToSnapshot = skipSnapshotsChecksum && jarFile.getName().contains("-SNAPSHOT");
+//            if (!skipDueToSnapshot && !matchesRegexChecksum(jarFile, skipRegexChecksum)) {
+//                // validate the jar matches against our expected hash
+//                final File checksumFile = new File(licenseDirectory, jarFile.getName()
+//                        + "." + CHECKSUM_TYPE);
+//                if (!(checksumFile.exists() && checksumFile.canRead())) {
+//                    log("MISSING " + CHECKSUM_TYPE + " checksum file for: "
+//                            + jarFile.getPath(), Project.MSG_ERR);
+//                    log("EXPECTED " + CHECKSUM_TYPE + " checksum file : "
+//                            + checksumFile.getPath(), Project.MSG_ERR);
+//                    this.failures = true;
+//                    return false;
+//                } else {
+//                    final String expectedChecksum = readChecksumFile(checksumFile);
+//                    try {
+//                        final MessageDigest md = MessageDigest.getInstance(CHECKSUM_TYPE);
+//                        byte[] buf = new byte[CHECKSUM_BUFFER_SIZE];
+//                        try {
+//                            FileInputStream fis = new FileInputStream(jarFile);
+//                            try {
+//                                DigestInputStream dis = new DigestInputStream(fis, md);
+//                                try {
+//                                    while (dis.read(buf, 0, CHECKSUM_BUFFER_SIZE) != -1) {
+//                                        // NOOP
+//                                    }
+//                                } finally {
+//                                    dis.close();
+//                                }
+//                            } finally {
+//                                fis.close();
+//                            }
+//                        } catch (IOException ioe) {
+//                            throw new BuildException("IO error computing checksum of file: "
+//                                    + jarFile, ioe);
+//                        }
+//                        final byte[] checksumBytes = md.digest();
+//                        final String checksum = createChecksumString(checksumBytes);
+//                        if (!checksum.equals(expectedChecksum)) {
+//                            log("CHECKSUM FAILED for " + jarFile.getPath() + " (expected: \""
+//                                            + expectedChecksum + "\" was: \"" + checksum + "\")",
+//                                    Project.MSG_ERR);
+//                            this.failures = true;
+//                            return false;
+//                        }
+//
+//                    } catch (NoSuchAlgorithmException ae) {
+//                        throw new BuildException("Digest type " + CHECKSUM_TYPE
+//                                + " not supported by your JVM", ae);
+//                    }
+//                }
+//            } else if (skipDueToSnapshot) {
+//                log("Skipping jar because it is a SNAPSHOT : "
+//                        + jarFile.getAbsolutePath(), Project.MSG_INFO);
+//            } else {
+//                log("Skipping jar because it matches regex pattern: "
+//                        + jarFile.getAbsolutePath() + " pattern: " + skipRegexChecksum.pattern(), Project.MSG_INFO);
+//            }
+//        }
+//
+//        // Get the expected license path base from the mapper and search for license files.
+//        Map<File, LicenseType> foundLicenses = new LinkedHashMap<>();
+//        List<File> expectedLocations = new ArrayList<>();
+//        outer:
+//        for (String mappedPath : licenseMapper.mapFileName(jarFile.getName())) {
+//            for (LicenseType licenseType : LicenseType.values()) {
+//                File licensePath = new File(licenseDirectory, mappedPath + licenseType.licenseFileSuffix());
+//                if (licensePath.exists()) {
+//                    foundLicenses.put(licensePath, licenseType);
+//                    log(" FOUND " + licenseType.name() + " license at " + licensePath.getPath(),
+//                            verboseLevel);
+//                    // We could continue scanning here to detect duplicate associations?
+//                    break outer;
+//                } else {
+//                    expectedLocations.add(licensePath);
+//                }
+//            }
+//        }
+//
+//        // Check for NOTICE files.
+//        for (Map.Entry<File, LicenseType> e : foundLicenses.entrySet()) {
+//            LicenseType license = e.getValue();
+//            String licensePath = e.getKey().getName();
+//            String baseName = licensePath.substring(
+//                    0, licensePath.length() - license.licenseFileSuffix().length());
+//            File noticeFile = new File(licenseDirectory, baseName + license.noticeFileSuffix());
+//
+//            if (noticeFile.exists()) {
+//                log(" FOUND NOTICE file at " + noticeFile.getAbsolutePath(), verboseLevel);
+//            } else {
+//                if (license.isNoticeRequired()) {
+//                    this.failures = true;
+//                    log("MISSING NOTICE for the license file:\n  "
+//                            + licensePath + "\n  Expected location below:\n  "
+//                            + noticeFile.getAbsolutePath(), Project.MSG_ERR);
+//                }
+//            }
+//        }
+//
+//        // In case there is something missing, complain.
+//        if (foundLicenses.isEmpty()) {
+//            this.failures = true;
+//            StringBuilder message = new StringBuilder();
+//            message.append(
+//                    "MISSING LICENSE for the following file:\n  " + jarFile.getAbsolutePath()
+//                            + "\n  Expected locations below:\n");
+//            for (File location : expectedLocations) {
+//                message.append("  => ").append(location.getAbsolutePath()).append("\n");
+//            }
+//            log(message.toString(), Project.MSG_ERR);
+//            return false;
+//        }
+//
+//        return true;
+//    }
+//
+//    private static final String createChecksumString(byte[] digest) {
+//        StringBuilder checksum = new StringBuilder();
+//        for (int i = 0; i < digest.length; i++) {
+//            checksum.append(String.format(Locale.ROOT, "%02x",
+//                    CHECKSUM_BYTE_MASK & digest[i]));
+//        }
+//        return checksum.toString();
+//    }
+//    private static final String readChecksumFile(File f) {
+//        BufferedReader reader = null;
+//        try {
+//            reader = new BufferedReader(new InputStreamReader
+//                    (new FileInputStream(f), StandardCharsets.UTF_8));
+//            try {
+//                String checksum = reader.readLine();
+//                if (null == checksum || 0 == checksum.length()) {
+//                    throw new BuildException("Failed to find checksum in file: " + f);
+//                }
+//                return checksum;
+//            } finally {
+//                reader.close();
+//            }
+//        } catch (IOException e) {
+//            throw new BuildException("IO error reading checksum file: " + f, e);
+//        }
+//    }
+//
+//    private static final boolean matchesRegexChecksum(File jarFile, Pattern skipRegexChecksum) {
+//        if (skipRegexChecksum == null) {
+//            return false;
+//        }
+//        Matcher m = skipRegexChecksum.matcher(jarFile.getName());
+//        return m.matches();
+//    }
+//}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseType.java
----------------------------------------------------------------------
diff --git a/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseType.java b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseType.java
new file mode 100644
index 0000000..8c1d2b8
--- /dev/null
+++ b/buildSrc/src/main/java/org/apache/lucene/gradle/LicenseType.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ * The ASF licenses this file to You 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 org.apache.lucene.gradle;
+
+
+/**
+ * A list of accepted licenses.  See also http://www.apache.org/legal/3party.html
+ *
+ **/
+public enum LicenseType {
+  ASL("Apache Software License 2.0", true),
+  BSD("Berkeley Software Distribution", true),
+  BSD_LIKE("BSD like license", true),//BSD like just means someone has taken the BSD license and put in their name, copyright, or it's a very similar license.
+  CDDL("Common Development and Distribution License", false),
+  CPL("Common Public License", true),
+  EPL("Eclipse Public License Version 1.0", false),
+  MIT("Massachusetts Institute of Tech. License", false),
+  MPL("Mozilla Public License", false), //NOT SURE on the required notice
+  PD("Public Domain", false),
+  //SUNBCLA("Sun Binary Code License Agreement"),
+  SUN("Sun Open Source License", false),
+  COMPOUND("Compound license (see NOTICE).", true),
+  FAKE("FAKE license - not needed", false);
+
+  private String display;
+  private boolean noticeRequired;
+
+  LicenseType(String display, boolean noticeRequired) {
+    this.display = display;
+    this.noticeRequired = noticeRequired;
+  }
+
+  public boolean isNoticeRequired() {
+    return noticeRequired;
+  }
+
+  public String getDisplay() {
+    return display;
+  }
+
+  public String toString() {
+    return "LicenseType{" +
+            "display='" + display + '\'' +
+            '}';
+  }
+
+  /**
+   * Expected license file suffix for a given license type.
+   */
+  public String licenseFileSuffix() {
+    return "-LICENSE-" + this.name() + ".txt";
+  }
+
+  /**
+   * Expected notice file suffix for a given license type.
+   */
+  public String noticeFileSuffix() {
+    return "-NOTICE.txt";
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/build.gradle
----------------------------------------------------------------------
diff --git a/lucene/build.gradle b/lucene/build.gradle
index e69de29..d772627 100644
--- a/lucene/build.gradle
+++ b/lucene/build.gradle
@@ -0,0 +1,8 @@
+import org.apache.lucene.gradle.LicenseCheckTask
+
+subprojects {
+    group = 'org.apache.lucene'
+}
+
+tasks.create("checkLicenses", LicenseCheckTask, new File(projectDir.getAbsolutePath()
+        + System.properties['file.separator'] + "licenses"))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/asm-tree-5.1.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/asm-tree-5.1.jar.sha1 b/lucene/licenses/asm-tree-5.1.jar.sha1
new file mode 100644
index 0000000..60d1311
--- /dev/null
+++ b/lucene/licenses/asm-tree-5.1.jar.sha1
@@ -0,0 +1 @@
+87b38c12a0ea645791ead9d3e74ae5268d1d6c34
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/commons-codec-1.9.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/commons-codec-1.9.jar.sha1 b/lucene/licenses/commons-codec-1.9.jar.sha1
new file mode 100644
index 0000000..5a14ad7
--- /dev/null
+++ b/lucene/licenses/commons-codec-1.9.jar.sha1
@@ -0,0 +1 @@
+9ce04e34240f674bc72680f8b843b1457383161a

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/commons-logging-1.1.3.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/commons-logging-1.1.3.jar.sha1 b/lucene/licenses/commons-logging-1.1.3.jar.sha1
deleted file mode 100644
index c8756c4..0000000
--- a/lucene/licenses/commons-logging-1.1.3.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-f6f66e966c70a83ffbdb6f17a0919eaf7c8aca7f

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/commons-logging-1.2.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/commons-logging-1.2.jar.sha1 b/lucene/licenses/commons-logging-1.2.jar.sha1
new file mode 100644
index 0000000..c314688
--- /dev/null
+++ b/lucene/licenses/commons-logging-1.2.jar.sha1
@@ -0,0 +1 @@
+4bfc12adfe4842bf07b657f0369c4cb522955686

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/hamcrest-core-1.1.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/hamcrest-core-1.1.jar.sha1 b/lucene/licenses/hamcrest-core-1.1.jar.sha1
new file mode 100644
index 0000000..6d004b6
--- /dev/null
+++ b/lucene/licenses/hamcrest-core-1.1.jar.sha1
@@ -0,0 +1 @@
+860340562250678d1a344907ac75754e259cdb14
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/hamcrest-core-LICENSE-BSD.txt
----------------------------------------------------------------------
diff --git a/lucene/licenses/hamcrest-core-LICENSE-BSD.txt b/lucene/licenses/hamcrest-core-LICENSE-BSD.txt
new file mode 100644
index 0000000..0bf6264
--- /dev/null
+++ b/lucene/licenses/hamcrest-core-LICENSE-BSD.txt
@@ -0,0 +1,27 @@
+BSD License
+
+Copyright (c) 2000-2015 www.hamcrest.org
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of
+conditions and the following disclaimer. Redistributions in binary form must reproduce
+the above copyright notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the distribution.
+
+Neither the name of Hamcrest nor the names of its contributors may be used to endorse
+or promote products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/hamcrest-core-NOTICE.txt
----------------------------------------------------------------------
diff --git a/lucene/licenses/hamcrest-core-NOTICE.txt b/lucene/licenses/hamcrest-core-NOTICE.txt
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/licenses/jetty-security-9.4.11.v20180605.jar.sha1
----------------------------------------------------------------------
diff --git a/lucene/licenses/jetty-security-9.4.11.v20180605.jar.sha1 b/lucene/licenses/jetty-security-9.4.11.v20180605.jar.sha1
new file mode 100644
index 0000000..8d8caad
--- /dev/null
+++ b/lucene/licenses/jetty-security-9.4.11.v20180605.jar.sha1
@@ -0,0 +1 @@
+926def86d31ee07ca4b4658833dc6ee6918b8e86
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/lucene/replicator/ivy.xml
----------------------------------------------------------------------
diff --git a/lucene/replicator/ivy.xml b/lucene/replicator/ivy.xml
index 24b053c..7732d44 100644
--- a/lucene/replicator/ivy.xml
+++ b/lucene/replicator/ivy.xml
@@ -39,8 +39,6 @@
     <dependency org="org.eclipse.jetty" name="jetty-continuation" rev="${/org.eclipse.jetty/jetty-continuation}" conf="jetty"/>
     <dependency org="org.eclipse.jetty" name="jetty-http" rev="${/org.eclipse.jetty/jetty-http}" conf="jetty"/>
 
-    <dependency org="commons-logging" name="commons-logging" rev="${/commons-logging/commons-logging}" conf="logging"/>
-
     <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>
   </dependencies>
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d541e180/solr/build.gradle
----------------------------------------------------------------------
diff --git a/solr/build.gradle b/solr/build.gradle
new file mode 100644
index 0000000..38c0584
--- /dev/null
+++ b/solr/build.gradle
@@ -0,0 +1,8 @@
+import org.apache.lucene.gradle.LicenseCheckTask
+
+subprojects {
+    group = 'org.apache.solr'
+}
+
+tasks.create("checkLicenses", LicenseCheckTask, new File(projectDir.getAbsolutePath()
+        + System.properties['file.separator'] + "licenses"))
\ No newline at end of file