import org.gradle.plugins.ide.eclipse.model.SourceFolder def luceneVersion = '10.5.0' def luceneRootRelativePath = 'third-party/lucene' def luceneSourceArtifacts = ['lucene-analysis-common', 'lucene-analyzers-common'] def luceneSourceDirectory = layout.buildDirectory.dir("${luceneRootRelativePath}/source/analyzers-common") def luceneGeneratedSourceDirectory = layout.buildDirectory.dir('generated/sources/lucene') def luceneGeneratedPorterFile = luceneGeneratedSourceDirectory.map { it.file('org/egothor/stemmer/benchmark/LucenePorterStemmerCopied.java') } def luceneSourceDownloadFile = layout.buildDirectory.file("${luceneRootRelativePath}/lucene-${luceneVersion}-sources.jar") dependencies { jmhImplementation "org.apache.lucene:lucene-analysis-common:${luceneVersion}" jmhImplementation "org.apache.lucene:lucene-analysis-stempel:${luceneVersion}" jmhImplementation "org.apache.lucene:lucene-analysis-morfologik:${luceneVersion}" } def buildLuceneSourcesName = { final String artifact -> "${artifact}-${luceneVersion}-sources.jar" } def buildLuceneSourcesUrl = { final String artifact -> "https://repo1.maven.org/maven2/org/apache/lucene/${artifact}/${luceneVersion}/${buildLuceneSourcesName(artifact)}" } def isLuceneSourcesDownloadable = { final String artifact -> try { final URL sourceUrl = new URL(buildLuceneSourcesUrl(artifact)) final java.net.HttpURLConnection connection = (java.net.HttpURLConnection) sourceUrl.openConnection() connection.requestMethod = 'HEAD' connection.instanceFollowRedirects = true connection.connectTimeout = 10000 connection.readTimeout = 10000 final int responseCode = connection.responseCode connection.disconnect() return responseCode == 200 } catch (Exception ignored) { return false } } def downloadLuceneSourcesJar = { -> final File targetFile = luceneSourceDownloadFile.get().asFile for (String artifact : luceneSourceArtifacts) { if (!isLuceneSourcesDownloadable(artifact)) { continue } final String sourceUrl = buildLuceneSourcesUrl(artifact) final File tempFile = new File(targetFile.parentFile, "${artifact}.${luceneVersion}.tmp") try { new URL(sourceUrl).withInputStream { inputStream -> tempFile.parentFile.mkdirs() tempFile.withOutputStream { outputStream -> outputStream << inputStream } } if (!tempFile.exists() || tempFile.length() <= 0L) { throw new GradleException("Downloaded Lucene source artifact for ${artifact} was empty.") } targetFile.delete() if (!tempFile.renameTo(targetFile)) { throw new GradleException("Failed to persist downloaded Lucene source artifact for ${artifact}.") } return } catch (Exception ignored) { tempFile.delete() } } throw new GradleException( "Failed to download Apache Lucene source artifacts ${luceneSourceArtifacts} for version ${luceneVersion}.") } def luceneSourceClasspathPath = provider { project.relativePath(luceneGeneratedSourceDirectory.get().asFile) } def luceneEclipseClasspathAttributes = [ gradle_scope : 'jmh', gradle_used_by_scope: 'jmh', test : 'true' ] def isAbsoluteClasspathPath = { String path -> path.startsWith('/') || path ==~ /^[A-Za-z]:[\\\/].*/ } def luceneGeneratedPorterNotice = ''' /** * Generated at benchmark execution time from Apache Lucene source. * * This source copy is compiled only for the JMH benchmark source set and is * not committed as production code. */ ''' def transformPorterStemmerSource = { final File sourceFile, final File targetFile -> if (!sourceFile.exists()) { throw new GradleException("Apache Lucene PorterStemmer source was not available at ${sourceFile}.") } final String sourceText = sourceFile.getText('UTF-8') String transformedText = sourceText if (transformedText.contains('package org.apache.lucene.analysis.en;')) { transformedText = transformedText.replaceFirst(/(?m)^\s*package\s+org\.apache\.lucene\.analysis\.en\s*;/, 'package org.egothor.stemmer.benchmark;') } else { throw new GradleException( 'Expected Lucene package-private PorterStemmer in org.apache.lucene.analysis.en package was not found in downloaded source.') } transformedText = transformedText.replaceFirst(/(?m)^\s*class\s+PorterStemmer\s*\{/, 'public final class LucenePorterStemmerCopied {') transformedText = transformedText.replaceFirst(/(?m)^\s*public\s+PorterStemmer\(\)/, 'public LucenePorterStemmerCopied()') if (!transformedText.contains('class LucenePorterStemmerCopied')) { throw new GradleException("Failed to rename PorterStemmer class when generating ${targetFile}.") } targetFile.parentFile.mkdirs() targetFile.text = transformedText } def resolveLucenePorterStemmerSource = { -> final File sourceRoot = luceneSourceDirectory.get().asFile final List candidates = [ 'org/apache/lucene/analysis/en/org/apache/lucene/analysis/en/PorterStemmer.java', 'org/apache/lucene/analysis/en/PorterStemmer.java', 'org/apache/lucene/analysis/en/org/tartarus/snowball/ext/PorterStemmer.java' ] for (String candidate : candidates) { final File file = new File(sourceRoot, candidate) if (file.exists()) { return file } } final FileTree porterCandidates = fileTree(sourceRoot).matching { include '**/PorterStemmer.java' } for (File file : porterCandidates.files) { if (file.text.contains('class PorterStemmer') && file.text.contains('package org.apache.lucene.analysis.en;')) { return file } } throw new GradleException('Unable to resolve Lucene PorterStemmer source file from extracted artifact.') } tasks.register('downloadLuceneAnalyzersSources') { group = 'build setup' description = 'Downloads Apache Lucene analysis sources for benchmark-only code generation.' outputs.file(luceneSourceDownloadFile) doLast { if (!luceneSourceDownloadFile.get().asFile.exists()) { downloadLuceneSourcesJar() } } } tasks.register('extractLuceneAnalyzersSources', Copy) { group = 'build setup' description = 'Extracts Apache Lucene analysis source JAR for benchmark-only extraction.' dependsOn(tasks.named('downloadLuceneAnalyzersSources')) from(zipTree(luceneSourceDownloadFile)) into(luceneSourceDirectory) } tasks.register('generateLucenePorterStemmerCopied') { group = 'build setup' description = 'Generates LucenePorterStemmerCopied into the build-only benchmark source directory.' dependsOn(tasks.named('extractLuceneAnalyzersSources')) inputs.dir(luceneSourceDirectory) outputs.file(luceneGeneratedPorterFile) doLast { final File sourceFile = resolveLucenePorterStemmerSource() transformPorterStemmerSource(sourceFile, luceneGeneratedPorterFile.get().asFile) } } sourceSets { jmh { java { srcDir(luceneGeneratedSourceDirectory) } } } tasks.named('compileJmhJava') { dependsOn(tasks.named('generateLucenePorterStemmerCopied')) } eclipse { classpath { file { whenMerged { classpath -> String generatedPath = luceneSourceClasspathPath.get() classpath.entries.removeAll { entry -> entry.hasProperty('path') && ( entry.path == generatedPath || isAbsoluteClasspathPath(entry.path) ) } SourceFolder luceneEntry = new SourceFolder(generatedPath, null) luceneEntry.output = 'bin/jmh' luceneEclipseClasspathAttributes.each { String name, String value -> luceneEntry.entryAttributes[name] = value } classpath.entries.add(luceneEntry) } } } }