diff --git a/aligner/build.gradle b/aligner/build.gradle index 4b41df8883..204865028c 100644 --- a/aligner/build.gradle +++ b/aligner/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/build-logic/build.gradle b/build-logic/build.gradle new file mode 100644 index 0000000000..78bf6e570f --- /dev/null +++ b/build-logic/build.gradle @@ -0,0 +1,14 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation 'com.github.ben-manes:gradle-versions-plugin:0.51.0' + implementation 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.26' + implementation 'com.diffplug.spotless:spotless-plugin-gradle:7.0.0.BETA4' + implementation 'edu.sc.seis.launch4j:launch4j:3.0.6' +} diff --git a/build-logic/src/main/groovy/org.omegat.common-utilities.gradle b/build-logic/src/main/groovy/org.omegat.common-utilities.gradle new file mode 100644 index 0000000000..c3766caa88 --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.common-utilities.gradle @@ -0,0 +1,48 @@ +import javax.inject.Inject + +interface InjectedExecOps { + @Inject + ExecOperations getExecOps() +} + +ext.loadProperties = { propFile -> + def config = new Properties() + if (propFile.canRead()) { + propFile.withInputStream { config.load(it) } + } + config +} + +ext { + exePresent = { exe -> + ["where $exe", "which $exe"].any { + try { + def findExe = it.execute() + findExe.waitForProcessOutput() + return findExe.exitValue() == 0 + } catch (any) { + return false + } + } + } + + conditions = { List... items -> + items.each { val, str -> + if (!val) { + logger.warn(str) + } + } + items.every { it[0] } + } + + condition = { val, str -> + conditions([val, str]) + } + + replaceRelativePathSegment = { FileCopyDetails deets, pattern, replacement -> + def segs = deets.relativePath.segments.collect { + it =~ pattern ? replacement : it + } + deets.relativePath = new RelativePath(!deets.directory, segs as String[]) + } +} diff --git a/build-logic/src/main/groovy/org.omegat.document-conventions.gradle b/build-logic/src/main/groovy/org.omegat.document-conventions.gradle new file mode 100644 index 0000000000..8f6dcd4e28 --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.document-conventions.gradle @@ -0,0 +1,151 @@ +plugins { + id 'org.omegat.common-utilities' +} + +tasks.register('manualZips') { + description = 'Build ZIP manuals to bundle into application. Requires container runtime.' + group = 'documentation' +} + +tasks.register('manualPdfs') { + description = 'Build PDF manuals for all languages. Requires container runtime.' + group = 'documentation' +} + +tasks.register('manualHtmls') { + description = 'Build HTML manuals and zip for all languages. Requires container runtime.' + group = 'documentation' +} + +tasks.register('genDocIndex', Copy) { + def docPropsFiles = fileTree(dir: 'doc_src', include: '*/version*.properties').findAll { + file("${it.parent}/OmegaTUsersManual_xinclude full.xml").file } + def langNameExceptions = loadProperties(file('doc_src/lang_exceptions.properties')) + def langInfos = docPropsFiles.toSorted{ it.parentFile.name }.collect { props -> + def docVersion = loadProperties(props).version + ['code': props.parentFile.name, 'nomanual': false, 'version': docVersion, + 'name': langNameExceptions[props.parentFile.name] ?: + Locale.forLanguageTag(props.parentFile.name.replace('_', '-')).getDisplayName(), + 'status': docVersion == omtVersion.version ? 'up-to-date' : 'out-of-date'] } + def inputTemplate = file('doc_src/index_template.html') + def outputIndex = layout.buildDirectory.file("docs/manual/index.html").get().asFile + description = 'Generate the docs index file' + inputs.files docPropsFiles, inputTemplate + outputs.files file(outputIndex) + from inputTemplate + into outputIndex.parent + rename('index_template.html', 'index.html') + expand('languages': langInfos) + filteringCharset = 'UTF-8' + dependsOn manualHtmls + group = 'documentation' +} + +tasks.register('webManual', Sync) { + group = 'documentation' + description = 'Sync the HTML manual files' + dependsOn manualHtmls, genDocIndex + destinationDir = file(layout.buildDirectory.file("docs/htdocs")) + from file(layout.buildDirectory.file("docs/manual")) + from('release') { + include 'doc-license.txt' + } +} + +ext.manualIndexXmls = fileTree(dir: 'doc_src', include: '**/OmegaTUsersManual_xinclude full.xml') +manualIndexXmls.each { xml -> + def lang = xml.parentFile.name + def pdfTaskName = "manualPdf${lang.capitalize()}" + tasks.register(pdfTaskName, Exec) { + inputs.files fileTree(dir: "doc_src/${lang}", includes: ['**/*.xml', 'images/*.png'], + excludes: ['xhtml5/*', 'index.xml']) + outputs.files layout.buildDirectory.file("docs/pdfs/OmegaT_documentation_${lang}.PDF") + onlyIf { + conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], + [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) + } + workingDir = 'doc_src' + commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/pdfs", 'pdf' + doLast { + delete fileTree(dir: "doc_src/${lang}", includes: ['pdf/*', 'index.xml']) + } + } + manualPdfs.dependsOn pdfTaskName + + def htmlTaskName = "manualHtml${lang.capitalize()}" + tasks.register(htmlTaskName, Exec) { + inputs.files fileTree(dir: "doc_src/${lang}", includes: ['**/*.xml', 'images/*.png'], + excludes: ['xhtml5/*', 'index.xml']) + outputs.files fileTree(dir: layout.buildDirectory.file("docs/manual/${lang}/"), + includes: ['*.html', 'OmegaT.css', 'images/*.png', '_wh/**/*.js', '_wh/wh.css']) + onlyIf { + conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], + [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) + } + workingDir = 'doc_src' + commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/manual/${lang}", 'html5' + } + manualHtmls.dependsOn htmlTaskName + + def zipTaskName="manualZip${lang.capitalize()}" + def versionProperties = loadProperties(file("doc_src/${lang}/version_${lang}.properties")) + if (lang.equals("en") || versionProperties.version.equals(omtVersion.version)) { + tasks.register(zipTaskName, Zip) { + from fileTree(dir: layout.buildDirectory.file("docs/manual/${lang}")) + exclude 'docs/manual/index.html' + from fileTree(dir: "doc_src/${lang}", include: '**/version*.properties') + archiveFileName = "${lang}.zip" + destinationDirectory = file("${buildDir}/docs/manuals/") + } + manualZips.dependsOn zipTaskName + tasks.getByName(zipTaskName).dependsOn htmlTaskName + } +} + +tasks.register('firstSteps') { + description = 'Build First pages for all languages at docs/greetings/. Requires Docker.' + group = 'documentation' +} + +tasks.register('updateManuals') { + group = 'documentation' + description = 'Update Instant Start guides and HTML manuals.' + dependsOn manualHtmls, firstSteps, genDocIndex +} + +ext.firstStepsXmls = fileTree(dir: 'doc_src', include: '**/First_Steps.xml') +firstStepsXmls.each { xml -> + def lang = xml.parentFile.name + def taskName = "firstSteps${lang.capitalize()}" + tasks.register(taskName, Exec) { + inputs.files fileTree(dir: "doc_src/${lang}", include: 'First_Steps.xml') + outputs.files fileTree(dir: layout.buildDirectory.file('docs/greetings/'), + includes: ["${lang}/first_steps.html", "${lang}/OmegaT.css"]) + onlyIf { + conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], + [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) + } + workingDir = 'doc_src' + commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/greetings/${lang}", 'first-steps' + } + + firstSteps.dependsOn taskName +} + +ext.instantStartXmls = fileTree(dir: 'doc_src', include: '**/InstantStartGuide.xml') +instantStartXmls.each { xml -> + def lang = xml.parentFile.name + def taskName = "instantStartGuide${lang.capitalize()}" + tasks.register(taskName, Exec) { + inputs.files fileTree(dir: "doc_src/${lang}", includes: ['InstantStartGuide.xml', '**/InstantGuide*png']) + outputs.files fileTree(dir: layout.buildDirectory.file('docs/greetings/'), + includes: ["${lang}/first_steps.html", "${lang}/images/InstantGuide*png", "${lang}/OmegaT.css"]) + onlyIf { + conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], + [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) + } + workingDir = 'doc_src' + commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/greetings/${lang}", 'instant-start' + } + firstSteps.dependsOn taskName +} diff --git a/build-logic/src/main/groovy/org.omegat.java-conventions.gradle b/build-logic/src/main/groovy/org.omegat.java-conventions.gradle new file mode 100644 index 0000000000..2ce235f2fb --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.java-conventions.gradle @@ -0,0 +1,146 @@ +import com.github.spotbugs.snom.Confidence + +plugins { + id 'java-library' + id 'eclipse' + id 'checkstyle' + id 'jacoco' + id 'com.github.ben-manes.versions' + id 'com.diffplug.spotless' + id 'com.github.spotbugs' + id 'org.omegat.common-utilities' +} + +// Definition of OmegaT versioning +def localPropsFile = file('local.properties') + +// Define target Java version to compatible. +def javaVersion = 11; + +// Flag to detect CI/CD environment +def envIsCi = project.hasProperty('envIsCi') as Boolean + +ext { + if (localPropsFile.file) { + loadProperties(localPropsFile).each { k, v -> + if (!findProperty(k)) { + set(k, v) + } + } + } + providedCoreLibsDir = file('lib/provided/core') + providedModuleLibsDir = file('lib/provided/module') +} + +repositories { + mavenCentral() + mavenLocal() +} + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(javaVersion) + vendor = JvmVendorSpec.ADOPTIUM + } +} + +tasks.withType(Test).configureEach { + workingDir project.rootProject.projectDir +} + +javadoc { + failOnError = false + options { + jFlags('-Duser.language=en') + addStringOption('locale', 'en_US') + addStringOption('bottom', 'Copyright 2000-2023, OmegaT project and contributors') + addStringOption('encoding', 'UTF-8') + addBooleanOption("Xdoclint:none", true) + addBooleanOption('html5', true) + addBooleanOption('frames', false) + addBooleanOption('public', true) + } +} + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" + options.compilerArgs.addAll '-Xlint', '-Werror' +} + +spotbugs { + reportLevel = Confidence.valueOf('HIGH') +} + +tasks.register('spotbugsMainReport') { + def reportFile = file("build/reports/spotbugs/main.txt") + doLast { + if (reportFile.exists()) { + println() + reportFile.readLines().forEach { + println(it) + } + } + } + group = 'verification' +} + +tasks.register('spotbugsTestReport') { + def reportFile = file("build/reports/spotbugs/test.txt") + doLast { + if (reportFile.exists()) { + println() + reportFile.readLines().forEach { + println(it) + } + } + } + group = 'verification' +} + +spotbugsMain { + if (envIsCi) { + extraArgs = ['-longBugCodes'] + jvmArgs = ['-Duser.language=en'] + } + reports { + text.required = envIsCi + html.required = !envIsCi + } + finalizedBy(spotbugsMainReport) +} + +spotbugsTest { + if (envIsCi) { + extraArgs = ['-longBugCodes'] + jvmArgs = ['-Duser.language=en'] + } + reports { + text.required = envIsCi + html.required = !envIsCi + } + finalizedBy(spotbugsTestReport) +} + +checkstyle { + toolVersion = libs.versions.checkstyle.get() +} +checkstyleMain.exclude '**/gen/**' + +spotless { + enforceCheck = false + java { + targetExclude 'src/gen/**' + eclipse().configFile file("${rootDir}/config/spotless/eclipse-formatting.xml") + removeUnusedImports() + } +} + +tasks.withType(Copy).configureEach { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} +tasks.withType(Sync).configureEach { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} +tasks.withType(Zip).configureEach { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} diff --git a/build-logic/src/main/groovy/org.omegat.jaxb-conventions.gradle b/build-logic/src/main/groovy/org.omegat.jaxb-conventions.gradle new file mode 100644 index 0000000000..dd041ec8ca --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.jaxb-conventions.gradle @@ -0,0 +1,39 @@ +repositories { + mavenCentral() +} + +configurations { + jaxb +} + +dependencies { + jaxb(libs.jaxb.xjc) +} + +tasks.register('genJAXB') { + description = 'Generate classes for loading and manipulating XML formats' + group = 'jaxb' +} + +ext.makeJaxbTask = { args -> + def taskName = "gen${args.name.capitalize()}" + tasks.register(taskName, JavaExec) { + classpath = configurations.jaxb + mainClass = 'com.sun.tools.xjc.XJCFacade' + delegate.args args.args + outputs.dir args.outdir + group = 'jaxb' + } + genJAXB.dependsOn taskName +} + +makeJaxbTask(name: 'segmentation', outdir: 'src/gen/core/segmentation', + args: ['-no-header', '-d', 'src', '-p', 'gen.core.segmentation', 'src/schemas/srx20.xsd']) +makeJaxbTask(name: 'filters', outdir: 'src/gen/core/filters', + args: ['-no-header', '-d', 'src', '-p', 'gen.core.filters', 'src/schemas/filters.xsd']) +makeJaxbTask(name: 'tbx', outdir: 'src/gen/core/tbx', + args: ['-no-header', '-d', 'src', '-p', 'gen.core.tbx', 'src/schemas/tbx.xsd']) +makeJaxbTask(name: 'project', outdir: 'src/gen/core/project', + args: ['-no-header', '-d', 'src', '-p', 'gen.core.project', 'src/schemas/project_properties.xsd']) +makeJaxbTask(name: 'tmx14', outdir: 'src/gen/core/tmx14', + args: ['-no-header', '-d', 'src', '-p', 'gen.core.tmx14', '-b', 'src/schemas/tmx14.xjb', 'src/schemas/tmx14.xsd']) diff --git a/build-logic/src/main/groovy/org.omegat.jpkg-conventions.gradle b/build-logic/src/main/groovy/org.omegat.jpkg-conventions.gradle new file mode 100644 index 0000000000..a5e36b4c6c --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.jpkg-conventions.gradle @@ -0,0 +1,178 @@ +import org.apache.tools.ant.filters.FixCrLfFilter +import org.apache.tools.ant.filters.ReplaceTokens + +import java.nio.file.Paths + +def omegatJarFilename = jar.archiveFileName.get() + +// clean work folder for jpackage +def jpackageWorkdir = layout.buildDirectory.file('jpackage') +tasks.register('cleanJpkgWorkdir', Delete) { + delete jpackageWorkdir +} + +tasks.register('jars', Sync) { + from configurations.runtimeClasspath + from(tasks.jar) { include omegatJarFilename } + into file(layout.buildDirectory.file('jars')) + dependsOn tasks.jar +} + +// prepare japckage contents +tasks.register('jpackageAppContentGreetings', Sync) { + dependsOn jpackage + dependsOn tasks.findByName('firstSteps') + + from layout.buildDirectory.file('docs/greetings') + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs/greetings") +} + +tasks.register('jpackageAppContentManuals', Sync) { + dependsOn jpackage + dependsOn tasks.findByName("manualZips") + + from layout.buildDirectory.file('docs/manuals') + include '*.zip' + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs/manuals") +} + +tasks.register('jpackageAppContentDocs', Copy) { + dependsOn jpackage + + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs") + from("release") { + include 'doc-license.txt' + filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) + } + from("release") { + exclude 'doc-license.txt' + include '*.txt', '*.html' + filter(ReplaceTokens, tokens: [ + TRANSLATION_NOTICE: '' + ]) + filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) + } +} +tasks.register('jpackageAppContentScripts', Sync) { + from 'scripts' + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/scripts") + dependsOn jpackage +} +tasks.register('jpackageAppContentImages', Sync) { + from 'images' + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/images") + dependsOn jpackage +} +tasks.register('jpackageAppContentModules', Sync) { + from layout.buildDirectory.file('modules') + into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/modules") + dependsOn subprojects.collect {it.tasks.withType(Jar)} + dependsOn jpackage +} +tasks.register('jpackageAppContent') { + dependsOn jpackageAppContentGreetings + dependsOn jpackageAppContentManuals + dependsOn jpackageAppContentDocs + dependsOn jpackageAppContentImages + dependsOn jpackageAppContentModules + dependsOn jpackageAppContentScripts +} + +// construct package contents in standard file tree +tasks.register('jpackage', Exec) { + dependsOn cleanJpkgWorkdir + dependsOn jars + group = 'other' + + inputs.files file(layout.buildDirectory.file('jars')) + outputs.files file(layout.buildDirectory.file("jpackage/app-image")) + onlyIf { gradleOnJava17OrLater } + + def osName = System.getProperty('os.name').toLowerCase() + def icon = 'images/OmegaT.png' + if (osName.contains('windows')) { + icon = "images/OmegaT.ico" + } else if (osName.contains('mac')) { + icon = "images/OmegaT.icns" + } + commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), + '--type', 'app-image', + '--app-version', version, + '--description', distDescription, + '--icon', icon, + '--name', application.applicationName, + '--dest', file(layout.buildDirectory.file("jpackage/app-image")), + '--vendor', distAppVendor, + '--input', file(layout.buildDirectory.file('jars')), + '--main-class', application.mainClass.get(), + '--main-jar', omegatJarFilename, + '--java-options', "-Xmx1024M", + '--java-options', "--add-opens", + '--java-options', "java.desktop/sun.awt.X11=ALL-UNNAMED", + '--resource-dir', file("release/jpackage-specific/linux/")) +} + +// generate DEB package +tasks.register("linuxDebDist", Exec) { + dependsOn jpackage + dependsOn jpackageAppContent + group = 'distribution' + def debVersion = '3' + inputs.files file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")) + outputs.files file(layout.buildDirectory + .file("distributions/${application.applicationName}_${version}-${debVersion}_amd64.deb")) + onlyIf { gradleOnJava17OrLater && exePresent('dpkg-deb') } + + commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), + '--type', 'deb', + '--app-version', version, + '--description', distDescription, + '--icon', file(layout.projectDirectory.file("images/OmegaT.png")), + '--name', application.applicationName, + '--app-image', file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")), + '--dest', file(layout.buildDirectory.file('distributions')), + '--vendor', distAppVendor, + '--resource-dir', file("release/jpackage-specific/linux/"), + '--about-url', "https://omegat.org/", + '--license-file', file(layout.projectDirectory.file("LICENSE")), + '--install-dir', '/opt/omegat-org', + '--linux-app-category', 'editors', + '--linux-app-release', debVersion, + '--linux-deb-maintainer', 'info@omegat.org', + '--linux-menu-group', 'Office', + '--linux-package-name', 'omegat', + '--linux-shortcut') + assemble.dependsOn linuxDebDist +} + +// generate RPM package +tasks.register("linuxRpmDist", Exec) { + dependsOn jpackage + dependsOn jpackageAppContent + group = 'distribution' + def rpmVersion = '3' + inputs.files file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")) + outputs.files file(layout.buildDirectory + .file("distributions/${application.applicationName}-${version}-${rpmVersion}.x86_64.rpm")) + onlyIf { gradleOnJava17OrLater && exePresent('rpm') } + + commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), + '--type', 'rpm', + '--app-version', version, + '--description', distDescription, + '--icon', file(layout.projectDirectory.file("images/OmegaT.png")), + '--name', application.applicationName, + '--app-image', file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")), + '--dest', file(layout.buildDirectory.file('distributions')), + '--vendor', distAppVendor, + '--about-url', "https://omegat.org/", + '--license-file', file(layout.projectDirectory.file("LICENSE")), + '--install-dir', '/opt/omegat-org', + '--linux-app-category', 'Application/Editors', + '--linux-app-release', rpmVersion, + '--linux-menu-group', 'Office', + '--linux-package-name', 'omegat', + '--linux-rpm-license-type', 'GPLv3+', + '--linux-shortcut') + assemble.dependsOn linuxRpmDist +} diff --git a/build-logic/src/main/groovy/org.omegat.main-utilities.gradle b/build-logic/src/main/groovy/org.omegat.main-utilities.gradle new file mode 100644 index 0000000000..5b28066a20 --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.main-utilities.gradle @@ -0,0 +1,47 @@ +plugins { + id 'org.omegat.common-utilities' +} + +tasks.register('changedOnBranch') { + description = 'List files that have been modified on this git branch.' + doLast { + def branch = providers.exec {commandLine("git", "name-rev", "--name-only", "HEAD")} + .standardOutput.asText.get().trim() + def exe = providers.exec { + ignoreExitValue = true + commandLine("git", "config", "branch.${branch}.pushRemote") + } + def remote = exe.result.get() == 0 ? exe.standardOutput.asText.get().trim() : '' + if (remote.empty) { + exe = providers.exec { + ignoreExitValue = true + commandLine("git", "config", "branch.${branch}.remote") + } + remote = exe.result.get() == 0 ? exe.standardOutput.asText.get().trim() : '' + } + if (remote.empty) { + logger.warn("Could not detect git remote name. Assuming 'origin'.") + remote = 'origin' + } + def splitPoint = providers.exec { + commandLine("git", "merge-base", "HEAD", "$remote/master") + }.standardOutput.asText.get().trim() + def gitModifiedFiles = providers.exec { + commandLine("git", "diff", "--name-only", "HEAD", "$splitPoint") + }.standardOutput.asText.get().trim().tokenize() + ext.files = project.files(gitModifiedFiles) + files.each { println(it) } + } +} + +tasks.register('spotlessChangedApply') { + description = 'Apply code formatting to files that have been changed on the current branch.' + group = 'omegat workflow' + finalizedBy 'spotlessApply' + dependsOn changedOnBranch + doFirst { + spotlessJava.target = changedOnBranch.files.findAll { + it.path.endsWith('.java') + } + } +} diff --git a/build-logic/src/main/groovy/org.omegat.version-conventions.gradle b/build-logic/src/main/groovy/org.omegat.version-conventions.gradle new file mode 100644 index 0000000000..7b45904837 --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.version-conventions.gradle @@ -0,0 +1,21 @@ +plugins { + id 'org.omegat.common-utilities' +} + +ext { + omtVersion = loadProperties(file('src/org/omegat/Version.properties')) + + getUpdateSuffix = { update -> + if (!update || update == '0') { + return '' + } + if (update.length() == 1) { + return "_0${update}" + } + "_${update}" + } +} + +tasks.register('printVersion') { + println project.version +} diff --git a/build-logic/src/main/groovy/org.omegat.windows-conventions.gradle b/build-logic/src/main/groovy/org.omegat.windows-conventions.gradle new file mode 100644 index 0000000000..e3c805eb0d --- /dev/null +++ b/build-logic/src/main/groovy/org.omegat.windows-conventions.gradle @@ -0,0 +1,52 @@ +plugins { + id 'edu.sc.seis.launch4j' + id 'org.omegat.version-conventions' +} + +// Read in all our custom messages and massage them for inclusion in the .iss +ext.getInnoSetupCustomMessages = { + // Don't include languages that InnoSetup doesn't have strings for + def blacklist = ['cy', 'ia', 'mfe'] + // Sort files to ensure English comes first, to set fallback + fileTree(dir: 'release/win32-specific', include: 'CustomMessages*.ini') + .sort() + .collect { file -> + def match = file.name =~ /CustomMessages_?([^\.]*).ini/ + if (match) { + def capture = match.group(1) + def lang = capture.empty ? 'en' : capture + if (!blacklist.contains(lang)) { + file.text.replaceAll(/(?m)^([^=]+)/) { "$lang.${it[0]}" } + } + } + }.findAll() + .join(System.lineSeparator()) +} + +/* + * Configuration of launch4j java launcher. + * OmegaT uses it as launcher for windows. + */ +launch4j { + libraryDir = "." // assume OmegaT.jar is located as same folder as OmegaT.exe + dontWrapJar = true + downloadUrl = 'https://adoptium.net/' + supportUrl = 'https://omegat.org/support' + icon = "${projectDir}/images/OmegaT.ico" + errTitle = 'OmegaT' + headerType = 'gui' + jreMinVersion = '11.0' + jreMaxVersion = '21.1' + copyConfigurable = [] // hack: don't copy dependencies to $libraryDir + // assume bundled JRE in jre/, fallback to JAVA_HOME env then PATH + bundledJrePath = 'jre;%JAVA_HOME%;%PATH%' + requires64Bit = false // support 32bit distribution + copyright = "The GNU General Public License, Version 3.0" + version = omtVersion.version + textVersion = omtVersion.version + companyName = distAppVendor + fileDescription = shortDescription + restartOnCrash = false + stayAlive = false + priority = 'normal' +} diff --git a/build.gradle b/build.gradle index 48981be880..baa0d13191 100644 --- a/build.gradle +++ b/build.gradle @@ -1,28 +1,23 @@ import org.apache.tools.ant.filters.FixCrLfFilter import org.apache.tools.ant.filters.ReplaceTokens -import java.nio.file.Paths -import com.github.spotbugs.snom.Confidence plugins { - id 'base' + id 'org.omegat.version-conventions' + id 'org.omegat.document-conventions' + id 'org.omegat.main-utilities' + id 'org.omegat.java-conventions' + id 'org.omegat.jaxb-conventions' + id 'org.omegat.windows-conventions' + id 'org.omegat.jpkg-conventions' id 'application' - id 'java-library' + id 'jvm-test-suite' id 'java-test-fixtures' id 'maven-publish' id 'signing' - id 'eclipse' - id 'checkstyle' - id 'jacoco' id 'jacoco-report-aggregation' - alias(libs.plugins.spotbugs) - alias(libs.plugins.spotless) - alias(libs.plugins.versions) - alias(libs.plugins.launch4j) alias(libs.plugins.ssh) } -apply from: 'gradle/utils.gradle' - application { applicationName = 'OmegaT' mainClass = 'org.omegat.Main' @@ -34,38 +29,10 @@ tasks.named('updateDaemonJvm') { jvmVersion = JavaLanguageVersion.of(17) } -// Define target Java version to compatible. -def javaVersion = 11; - -// OmegaT distribution package meta data. -def shortDescription = 'The free translation memory tool' -def distDescription = 'OmegaT is a free and open source multiplatform Computer Assisted Translation tool with' + - ' fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated' + - ' projects.' -def distAppVendor = 'The OmegaT project' - -// Definition of OmegaT versioning -def localPropsFile = file('local.properties') -ext { - omtVersion = loadProperties(file('src/org/omegat/Version.properties')) - if (localPropsFile.file) { - loadProperties(localPropsFile).each { k, v -> - if (!findProperty(k)) { - set(k, v) - } - } - } - providedCoreLibsDir = file('lib/provided/core') - providedModuleLibsDir = file('lib/provided/module') -} def omtFlavor = omtVersion.beta.empty ? 'standard' : 'latest' -def omtWebsite = 'https://omegat.org' version = omtVersion.version + getUpdateSuffix(omtVersion.update) -// Flag to detect CI/CD environment -def envIsCi = project.hasProperty('envIsCi') as Boolean - // Definition of bundled JRE file names def assetDir = findProperty('assetDir') ?: '../' def macJRE = fileTree(dir: assetDir, include: 'OpenJDK17U-jre_x64_mac_*.tar.gz') @@ -80,118 +47,8 @@ java { withJavadocJar() } -allprojects { - apply plugin: 'checkstyle' - apply plugin: 'java-library' - apply plugin: 'eclipse' - apply plugin: 'com.github.spotbugs' - apply plugin: 'com.diffplug.spotless' - apply plugin: 'jacoco' - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(javaVersion) - vendor = JvmVendorSpec.ADOPTIUM - } - } - - javadoc { - failOnError = false - options { - jFlags('-Duser.language=en') - addStringOption('locale', 'en_US') - addStringOption('bottom', 'Copyright 2000-2023, OmegaT project and contributors') - addStringOption('encoding', 'UTF-8') - addBooleanOption("Xdoclint:none", true) - addBooleanOption('html5', true) - addBooleanOption('frames', false) - addBooleanOption('public', true) - } - } - tasks.withType(JavaCompile) { - options.encoding = "UTF-8" - options.compilerArgs.addAll '-Xlint', '-Werror' - } - - sourcesJar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - } - - spotbugs { - reportLevel = Confidence.valueOf('HIGH') - } - - tasks.register('spotbugsMainReport') { - def reportFile = file("build/reports/spotbugs/main.txt") - doLast { - if (reportFile.exists()) { - println() - reportFile.readLines().forEach { - println(it) - } - } - } - group = 'verification' - } - - tasks.register('spotbugsTestReport') { - def reportFile = file("build/reports/spotbugs/test.txt") - doLast { - if (reportFile.exists()) { - println() - reportFile.readLines().forEach { - println(it) - } - } - } - group = 'verification' - } - - spotbugsMain { - if (envIsCi) { - extraArgs = ['-longBugCodes'] - jvmArgs = ['-Duser.language=en'] - } - reports { - text.required = envIsCi - html.required = !envIsCi - } - finalizedBy(spotbugsMainReport) - } - - spotbugsTest { - if (envIsCi) { - extraArgs = ['-longBugCodes'] - jvmArgs = ['-Duser.language=en'] - } - reports { - text.required = envIsCi - html.required = !envIsCi - } - finalizedBy(spotbugsTestReport) - } - - checkstyle { - toolVersion = libs.versions.checkstyle.get() - } - checkstyleMain.exclude '**/gen/**' - - spotless { - enforceCheck false - java { - targetExclude 'src/gen/**' - eclipse().configFile file("${rootDir}/config/spotless/eclipse-formatting.xml") - removeUnusedImports() - } - } - - repositories { - mavenCentral() - mavenLocal() - // Sonatype OSSRH snapshots - maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" } - maven { url "https://oss.sonatype.org/content/repositories/snapshots" } - } +sourcesJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } sourceSets { @@ -219,8 +76,6 @@ sourceSets { } testAcceptance { java { - compileClasspath += main.output + test.output - runtimeClasspath += main.output + test.output srcDir 'test-acceptance/src' } } @@ -237,7 +92,6 @@ configurations { testIntegrationImplementation.extendsFrom implementation testAcceptanceImplementation.extendsFrom testImplementation testAcceptanceRuntime.extendsFrom testRuntime - jaxb genMac } @@ -360,28 +214,29 @@ dependencies { } // Test dependencies - testFixturesApi(libs.junit4) // for http connection test testFixturesApi(libs.wiremock) { exclude module: 'guava' } testFixturesApi(libs.slf4j.api) + testFixturesImplementation sourceSets.main.output testFixturesImplementation(libs.commons.io) testFixturesImplementation(libs.omegat.vldocking) testFixturesImplementation(libs.assertj.swing.junit) + testImplementation(libs.junit4) + testImplementation(libs.commons.io) testImplementation(libs.assertj) testImplementation(libs.bundles.xmlunit) - + testImplementation(libs.slf4j.api) testImplementation(libs.languagetool.server) { exclude module: "logback-classic" } + testRuntimeOnly(libs.bundles.groovy) + testRuntimeOnly(libs.nashorn.core) testRuntimeOnly(libs.slf4j.jdk14) - // JAXB codegen only - jaxb(libs.jaxb.xjc) - // genMac only genMac(libs.omegat.appbundler) @@ -408,6 +263,16 @@ dependencies { jacocoAggregation project(":machinetranslators:yandex") } +test { + // Test in headless mode with ./gradlew test -Pheadless + if (project.hasProperty('headless')) { + systemProperty 'java.awt.headless', 'true' + } + systemProperty 'java.util.logging.config.file', "${rootDir}/config/test/logger.properties" + // some test case depends on modules from subproject + dependsOn subprojects.collect { it.tasks.withType(Jar) } +} + jar { def omtPlugins = loadProperties(file('Plugins.properties')) manifest { @@ -453,182 +318,6 @@ def omegatJarFilename = jar.archiveFileName.get() project(":machinetranslators") {jar.enabled = false} project(":spellchecker") {jar.enabled = false} -/* - * Configuration of launch4j java launcher. - * OmegaT uses it as launcher for windows. - */ -launch4j { - libraryDir = "." // assume OmegaT.jar is located as same folder as OmegaT.exe - dontWrapJar = true - downloadUrl = 'https://adoptium.net/' - supportUrl = 'https://omegat.org/support' - icon = "${projectDir}/images/OmegaT.ico" - errTitle = 'OmegaT' - headerType = 'gui' - jreMinVersion = '11.0' - jreMaxVersion = '21.1' - copyConfigurable = [] // hack: don't copy dependencies to $libraryDir - // assume bundled JRE in jre/, fallback to JAVA_HOME env then PATH - bundledJrePath = 'jre;%JAVA_HOME%;%PATH%' - requires64Bit = false // support 32bit distribution - copyright = "The GNU General Public License, Version 3.0" - version = omtVersion.version - textVersion = omtVersion.version - companyName = distAppVendor - fileDescription = shortDescription - restartOnCrash = false - stayAlive = false - priority = 'normal' -} - -tasks.register('manualZips') { - description = 'Build ZIP manuals to bundle into application. Requires container runtime.' - group = 'documentation' -} - -tasks.register('manualPdfs') { - description = 'Build PDF manuals for all languages. Requires container runtime.' - group = 'documentation' -} - -tasks.register('manualHtmls') { - description = 'Build HTML manuals and zip for all languages. Requires container runtime.' - group = 'documentation' -} - -tasks.register('genDocIndex', Copy) { - def docPropsFiles = fileTree(dir: 'doc_src', include: '*/version*.properties').findAll { - file("${it.parent}/OmegaTUsersManual_xinclude full.xml").file } - def langNameExceptions = loadProperties(file('doc_src/lang_exceptions.properties')) - def langInfos = docPropsFiles.toSorted{ it.parentFile.name }.collect { props -> - def docVersion = loadProperties(props).version - ['code': props.parentFile.name, 'nomanual': false, 'version': docVersion, - 'name': langNameExceptions[props.parentFile.name] ?: - Locale.forLanguageTag(props.parentFile.name.replace('_', '-')).getDisplayName(), - 'status': docVersion == omtVersion.version ? 'up-to-date' : 'out-of-date'] } - def inputTemplate = file('doc_src/index_template.html') - def outputIndex = layout.buildDirectory.file("docs/manual/index.html").get().asFile - description = 'Generate the docs index file' - inputs.files docPropsFiles, inputTemplate - outputs.files file(outputIndex) - from inputTemplate - into outputIndex.parent - rename('index_template.html', 'index.html') - expand('languages': langInfos) - filteringCharset = 'UTF-8' - dependsOn manualHtmls - group = 'documentation' -} - -tasks.register('webManual', Sync) { - group = 'documentation' - description = 'Sync the HTML manual files' - dependsOn manualHtmls, genDocIndex - destinationDir file(layout.buildDirectory.file("docs/htdocs")) - from file(layout.buildDirectory.file("docs/manual")) - from('release') { - include 'doc-license.txt' - } -} - -ext.manualIndexXmls = fileTree(dir: 'doc_src', include: '**/OmegaTUsersManual_xinclude full.xml') -manualIndexXmls.each { xml -> - def lang = xml.parentFile.name - def pdfTaskName = "manualPdf${lang.capitalize()}" - tasks.register(pdfTaskName, Exec) { - inputs.files fileTree(dir: "doc_src/${lang}", includes: ['**/*.xml', 'images/*.png'], - excludes: ['xhtml5/*', 'index.xml']) - outputs.files layout.buildDirectory.file("docs/pdfs/OmegaT_documentation_${lang}.PDF") - onlyIf { - conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], - [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) - } - workingDir = 'doc_src' - commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/pdfs", 'pdf' - doLast { - delete fileTree(dir: "doc_src/${lang}", includes: ['pdf/*', 'index.xml']) - } - } - manualPdfs.dependsOn pdfTaskName - - def htmlTaskName = "manualHtml${lang.capitalize()}" - tasks.register(htmlTaskName, Exec) { - inputs.files fileTree(dir: "doc_src/${lang}", includes: ['**/*.xml', 'images/*.png'], - excludes: ['xhtml5/*', 'index.xml']) - outputs.files fileTree(dir: layout.buildDirectory.file("docs/manual/${lang}/"), - includes: ['*.html', 'OmegaT.css', 'images/*.png', '_wh/**/*.js', '_wh/wh.css']) - onlyIf { - conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], - [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) - } - workingDir = 'doc_src' - commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/manual/${lang}", 'html5' - } - manualHtmls.dependsOn htmlTaskName - - def zipTaskName="manualZip${lang.capitalize()}" - def versionProperties = loadProperties(file("doc_src/${lang}/version_${lang}.properties")) - if (lang.equals("en") || versionProperties.version.equals(omtVersion.version)) { - tasks.register(zipTaskName, Zip) { - from fileTree(dir: layout.buildDirectory.file("docs/manual/${lang}")) - exclude 'docs/manual/index.html' - from fileTree(dir: "doc_src/${lang}", include: '**/version*.properties') - archiveFileName = "${lang}.zip" - destinationDirectory = file("${buildDir}/docs/manuals/") - } - manualZips.dependsOn zipTaskName - tasks.getByName(zipTaskName).dependsOn htmlTaskName - } -} - -tasks.register('firstSteps') { - description = 'Build First pages for all languages at docs/greetings/. Requires Docker.' - group = 'documentation' -} - -tasks.register('updateManuals') { - group = 'documentation' - description = 'Update Instant Start guides and HTML manuals.' - dependsOn manualHtmls, firstSteps, genDocIndex -} - -ext.firstStepsXmls = fileTree(dir: 'doc_src', include: '**/First_Steps.xml') -firstStepsXmls.each { xml -> - def lang = xml.parentFile.name - def taskName = "firstSteps${lang.capitalize()}" - tasks.register(taskName, Exec) { - inputs.files fileTree(dir: "doc_src/${lang}", include: 'First_Steps.xml') - outputs.files fileTree(dir: layout.buildDirectory.file('docs/greetings/'), - includes: ["${lang}/first_steps.html", "${lang}/OmegaT.css"]) - onlyIf { - conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], - [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) - } - workingDir = 'doc_src' - commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/greetings/${lang}", 'first-steps' - } - - firstSteps.dependsOn taskName -} - -ext.instantStartXmls = fileTree(dir: 'doc_src', include: '**/InstantStartGuide.xml') -instantStartXmls.each { xml -> - def lang = xml.parentFile.name - def taskName = "instantStartGuide${lang.capitalize()}" - tasks.register(taskName, Exec) { - inputs.files fileTree(dir: "doc_src/${lang}", includes: ['InstantStartGuide.xml', '**/InstantGuide*png']) - outputs.files fileTree(dir: layout.buildDirectory.file('docs/greetings/'), - includes: ["${lang}/first_steps.html", "${lang}/images/InstantGuide*png", "${lang}/OmegaT.css"]) - onlyIf { - conditions([exePresent('docker') || exePresent('nerdctl'), 'Docker or nerdctl is not installed'], - [!project.hasProperty('forceSkipDocumentBuild'), 'Specified forceSkipDocumentBuild property']) - } - workingDir = 'doc_src' - commandLine './docgen', "-Dlanguage=${lang}", "-Dtarget=../build/docs/greetings/${lang}", 'instant-start' - } - firstSteps.dependsOn taskName -} - tasks.register('genMac') { def appbundlerClasspath = configurations.genMac.asPath def outDir = layout.buildDirectory.file("appbundler").get().toString() @@ -712,7 +401,9 @@ distributions { VERSION_NUMBER_SUBST: project.version, JAR_SUBST : omegatJarFilename ] - fileMode 0755 + filePermissions { + unix(0755) + } } from('release/win32-specific') { include 'OmegaT.bat' @@ -781,10 +472,11 @@ tasks.register('hunspellJarSignedContents', Sync) { } from zipTree(hunspellJar) - destinationDir file(layout.buildDirectory.file("hunspell")) + destinationDir = file(layout.buildDirectory.file("hunspell")) doLast { def dylibs = fileTree(dir: destinationDir, include: '**/*.dylib').files - exec { + def injected = project.objects.newInstance(InjectedExecOps) + injected.execOps.exec { commandLine('codesign', '--deep', '--force', '--sign', project.property('macCodesignIdentity'), '--timestamp', @@ -821,7 +513,9 @@ ext.makeMacTask = { args -> } from(genMac.outputs) { include '**/MacOS/OmegaT' - fileMode 0755 + filePermissions { + unix(0755) + } } from(genMac.outputs) { include '**/Info.plist' @@ -872,7 +566,9 @@ ext.makeMacTask = { args -> } from(genMac.outputs) { include '**/MacOS/OmegaT' - fileMode 0755 + filePermissions { + unix(0755) + } } from(genMac.outputs) { include '**/Info.plist' @@ -895,7 +591,7 @@ ext.makeMacTask = { args -> } } duplicatesStrategy = DuplicatesStrategy.INCLUDE - destinationDir file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) + destinationDir = file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) outputs.upToDateWhen { // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar def f1 = file("$destinationDir/OmegaT.app/Contents/Java/OmegaT.jar") @@ -922,12 +618,13 @@ ext.makeMacTask = { args -> into 'OmegaT.app/Contents/Java/lib' } duplicatesStrategy = DuplicatesStrategy.INCLUDE - destinationDir file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}_Signed")) + destinationDir = file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}_Signed")) doFirst { delete "$destinationDir/OmegaT.app/Contents/PlugIns/jre.bundle" } doLast { - exec { + def injected = project.objects.newInstance(InjectedExecOps) + injected.execOps.exec { commandLine 'codesign', '--deep', '--force', '--sign', project.property('macCodesignIdentity'), '--timestamp', @@ -956,7 +653,8 @@ ext.makeMacTask = { args -> } inputs.files tasks.getByName(signedZipTaskName).outputs.files doLast { - exec { + def injected = project.objects.newInstance(InjectedExecOps) + injected.execOps.exec { // Assuming setup per instructions at // https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow#3087734 commandLine 'xcrun', 'altool', '--notarize-app', @@ -977,12 +675,13 @@ ext.makeMacTask = { args -> condition(exePresent('xcrun'), 'XCode is not present in system.') } doFirst { + def injected = project.objects.newInstance(InjectedExecOps) if (args.name.equals("mac")) { - exec { + injected.execOps.exec { commandLine 'xcrun', 'stapler', 'staple', "${macInstallSignedDist.destinationDir}/OmegaT.app" } } else { - exec { + injected.execOps.exec { commandLine 'xcrun', 'stapler', 'staple', "${armMacInstallSignedDist.destinationDir}/OmegaT.app" } } @@ -997,6 +696,8 @@ makeMacTask(name: 'macArm', suffix: 'Mac_arm', jrePath: armMacJRE) tasks.register('linux') { description = 'Build the Linux distributions.' group = 'omegat distribution' + dependsOn linuxDebDist + dependsOn linuxRpmDist } ext.makeLinuxTask = { args -> @@ -1006,7 +707,7 @@ ext.makeLinuxTask = { args -> group = 'distribution' description = "Create a linux installDist for ${args.name}. " with distributions.main.contents - destinationDir file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) + destinationDir = file(layout.buildDirectory.file("install/${application.applicationName}-${args.suffix}")) onlyIf { condition(!args.jrePath.empty, 'ARM64 JRE not found') } @@ -1049,365 +750,16 @@ ext.makeLinuxTask = { args -> makeLinuxTask(name: "linux64", suffix: "Linux_64", jrePath: linux64JRE) makeLinuxTask(name: "linuxArm64", suffix: "Linux_ARM64", jrePath: linuxArm64JRE) -// clean work folder for jpackage -def jpackageWorkdir = layout.buildDirectory.file('jpackage') -tasks.register('cleanJpkgWorkdir', Delete) { - delete jpackageWorkdir -} - -tasks.register('jars', Sync) { - from configurations.runtimeClasspath - from(tasks.jar) { include omegatJarFilename } - into file(layout.buildDirectory.file('jars')) - dependsOn tasks.jar -} - -// prepare japckage contents -tasks.register('jpackageAppContentGreetings', Sync) { - dependsOn jpackage, firstSteps - - from layout.buildDirectory.file('docs/greetings') - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs/greetings") -} - -tasks.register('jpackageAppContentManuals', Sync) { - dependsOn jpackage, manualZips - - from layout.buildDirectory.file('docs/manuals') - include '*.zip' - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs/manuals") -} - -tasks.register('jpackageAppContentDocs', Copy) { - dependsOn jpackage - - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/docs") - from("release") { - include 'doc-license.txt' - filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) - } - from("release") { - exclude 'doc-license.txt' - include '*.txt', '*.html' - filter(ReplaceTokens, tokens: [ - TRANSLATION_NOTICE: '' - ]) - filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) - } -} -tasks.register('jpackageAppContentScripts', Sync) { - from 'scripts' - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/scripts") - dependsOn jpackage -} -tasks.register('jpackageAppContentImages', Sync) { - from 'images' - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/images") - dependsOn jpackage -} -tasks.register('jpackageAppContentModules', Sync) { - from layout.buildDirectory.file('modules') - into layout.buildDirectory.file("jpackage/app-image/${application.applicationName}/lib/modules") - dependsOn subprojects.collect {it.tasks.withType(Jar)} - dependsOn jpackage -} -tasks.register('jpackageAppContent') { - dependsOn jpackageAppContentGreetings - dependsOn jpackageAppContentManuals - dependsOn jpackageAppContentDocs - dependsOn jpackageAppContentImages - dependsOn jpackageAppContentModules - dependsOn jpackageAppContentScripts -} - -// construct package contents in standard file tree -tasks.register('jpackage', Exec) { - dependsOn cleanJpkgWorkdir - dependsOn jars - group 'other' - - inputs.files file(layout.buildDirectory.file('jars')) - outputs.files file(layout.buildDirectory.file("jpackage/app-image")) - onlyIf { gradleOnJava17OrLater } - - def osName = System.getProperty('os.name').toLowerCase() - def icon = 'images/OmegaT.png' - if (osName.contains('windows')) { - icon = "images/OmegaT.ico" - } else if (osName.contains('mac')) { - icon = "images/OmegaT.icns" - } - commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), - '--type', 'app-image', - '--app-version', version, - '--description', distDescription, - '--icon', icon, - '--name', application.applicationName, - '--dest', file(layout.buildDirectory.file("jpackage/app-image")), - '--vendor', distAppVendor, - '--input', file(layout.buildDirectory.file('jars')), - '--main-class', application.mainClass.get(), - '--main-jar', omegatJarFilename, - '--java-options', "-Xmx1024M", - '--java-options', "--add-opens", - '--java-options', "java.desktop/sun.awt.X11=ALL-UNNAMED", - '--resource-dir', file("release/jpackage-specific/linux/")) -} - -// generate DEB package -tasks.register("linuxDebDist", Exec) { - dependsOn jpackage - dependsOn jpackageAppContent - group 'distribution' - def debVersion = '3' - inputs.files file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")) - outputs.files file(layout.buildDirectory - .file("distributions/${application.applicationName}_${version}-${debVersion}_amd64.deb")) - onlyIf { gradleOnJava17OrLater && exePresent('dpkg-deb') } - - commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), - '--type', 'deb', - '--app-version', version, - '--description', distDescription, - '--icon', file(layout.projectDirectory.file("images/OmegaT.png")), - '--name', application.applicationName, - '--app-image', file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")), - '--dest', file(layout.buildDirectory.file('distributions')), - '--vendor', distAppVendor, - '--resource-dir', file("release/jpackage-specific/linux/"), - '--about-url', "https://omegat.org/", - '--license-file', file(layout.projectDirectory.file("LICENSE")), - '--install-dir', '/opt/omegat-org', - '--linux-app-category', 'editors', - '--linux-app-release', debVersion, - '--linux-deb-maintainer', 'info@omegat.org', - '--linux-menu-group', 'Office', - '--linux-package-name', 'omegat', - '--linux-shortcut') - tasks.getByName('linux').dependsOn linuxDebDist - assemble.dependsOn linuxDebDist -} - -// generate RPM package -tasks.register("linuxRpmDist", Exec) { - dependsOn jpackage - dependsOn jpackageAppContent - group 'distribution' - def rpmVersion = '3' - inputs.files file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")) - outputs.files file(layout.buildDirectory - .file("distributions/${application.applicationName}-${version}-${rpmVersion}.x86_64.rpm")) - onlyIf { gradleOnJava17OrLater && exePresent('rpm') } - - commandLine(file(Paths.get(System.getProperty('java.home')).resolve("bin/jpackage")), - '--type', 'rpm', - '--app-version', version, - '--description', distDescription, - '--icon', file(layout.projectDirectory.file("images/OmegaT.png")), - '--name', application.applicationName, - '--app-image', file(layout.buildDirectory.file("jpackage/app-image/${application.applicationName}")), - '--dest', file(layout.buildDirectory.file('distributions')), - '--vendor', distAppVendor, - '--about-url', "https://omegat.org/", - '--license-file', file(layout.projectDirectory.file("LICENSE")), - '--install-dir', '/opt/omegat-org', - '--linux-app-category', 'Application/Editors', - '--linux-app-release', rpmVersion, - '--linux-menu-group', 'Office', - '--linux-package-name', 'omegat', - '--linux-rpm-license-type', 'GPLv3+', - '--linux-shortcut') - tasks.getByName('linux').dependsOn linuxRpmDist - assemble.dependsOn linuxRpmDist -} - // We bundle our startup scripts separately, so disable startScripts. startScripts.enabled = false // installDist insists on destination executable directory even when disable start script. application.executableDir = "" -// Read in all our custom messages and massage them for inclusion in the .iss -ext.getInnoSetupCustomMessages = { - // Don't include languages that InnoSetup doesn't have strings for - def blacklist = ['cy', 'ia', 'mfe'] - // Sort files to ensure English comes first, to set fallback - fileTree(dir: 'release/win32-specific', include: 'CustomMessages*.ini') - .sort() - .collect { file -> - def match = file.name =~ /CustomMessages_?([^\.]*).ini/ - if (match) { - def capture = match.group(1) - def lang = capture.empty ? 'en' : capture - if (!blacklist.contains(lang)) { - file.text.replaceAll(/(?m)^([^=]+)/) { "$lang.${it[0]}" } - } - } - }.findAll() - .join(System.lineSeparator()) -} - tasks.register('win') { description = 'Build the Windows distributions.' group = 'omegat distribution' } -ext.makeWinTask = { args -> - def fullVersion = project.version + omtVersion.beta - def installerBasename = "OmegaT_${fullVersion}_${args.suffix}" - def installerWinExe = base.distsDirectory.file("${installerBasename}.exe") - def signedWinExe = base.distsDirectory.file("${installerBasename}_Signed.exe") - def prepDistsTaskName = "${args.name}Prep" - def genDistsTaskName = "${args.name}Gen" - def distsTaskName = "${args.name}" - def signedTaskName = "${args.name}Signed" - def signedTaskCommandArgs = { arg2 -> - def exe = exePresent('osslsigncode') ? 'osslsigncode' : file('ci/osslsigncode').toString() - def commandArgs = [exe, 'sign'] - if (project.hasProperty('pkcs11module')) { - commandArgs.addAll('-pkcs11module', project.property('pkcs11module')) - } - if (project.hasProperty('winCodesignCert')) { - commandArgs.addAll('-certs', project.property('winCodesignCert')) - } - if (project.hasProperty('pkcs11cert')) { - commandArgs.addAll('-pkcs11cert', project.property('pkcs11cert')) - } - if (project.hasProperty('winCodesignPassword')) { - commandArgs.addAll('-pass', project.property('winCodesignPassword')) - } - if (project.hasProperty('winCodesignDevice')) { - envVars['USBDEV'] = project.property('winCodesignDevice') - } - commandArgs.addAll( - '-t', project.hasProperty('winCodesignTimestampUrl') ? project.property('winCodesignTimestampUrl') : - 'http://time.certum.pl/', - '-n', application.applicationName, '-i', omtWebsite, '-h', 'sha256', - '-in', installerWinExe.get().asFile, - '-out', signedWinExe.get().asFile - ) - return commandArgs - } - - tasks.register(prepDistsTaskName, Sync) { - onlyIf { - conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], - [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), - 'InnoSetup or Docker not installed']) - } - doFirst { - delete "$destinationDir/jre" - delete installerWinExe - } - with distributions.main.contents - destinationDir file(layout.buildDirectory.file("innosetup/${args.name}")) - outputs.upToDateWhen { - // detect up-to-date when OmegaT.jar exists and newer than libs/OmegaT.jar - def f1 = layout.buildDirectory.file("innosetup/${args.name}/OmegaT.jar").get().asFile - def f2 = base.libsDirectory.file('OmegaT.jar').get().asFile - f1.exists() && f2.exists() && f1.lastModified() > f2.lastModified() - } - from('release/win32-specific') { - include 'OmegaT.l4J.ini' - include 'OmegaT.iss' - filter(ReplaceTokens, tokens: [ - VERSION_NUMBER_SUBST : fullVersion, - OUTPUT_BASENAME_SUBST: installerBasename.toString(), - CUSTOM_MESSAGES_SUBST: getInnoSetupCustomMessages(), - ARCHITECTURE_SUBST : args.arch ?: '' - ]) - filter(FixCrLfFilter, eol: FixCrLfFilter.CrLf.newInstance('crlf')) - filteringCharset = 'UTF-8' - } - from('build/launch4j') { - include '*.exe' - } - if (args.jrePath && !args.jrePath.empty) { - from(zipTree(args.jrePath.singleFile)) { - includeEmptyDirs = false - eachFile { - replaceRelativePathSegment(it, /jdk.*-jre/, 'jre') - } - } - } - dependsOn createAllExecutables - } - - tasks.register(genDistsTaskName, Exec) { - onlyIf { - conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], - [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), - 'InnoSetup or Docker not installed']) - } - dependsOn prepDistsTaskName - inputs.files( - layout.buildDirectory.file("innosetup/${args.name}/OmegaT.jar"), - layout.buildDirectory.file("innosetup/${args.name}/OmegaT.iss"), - layout.buildDirectory.file("innosetup/${args.name}/OmegaT.l4j.ini"), - ) - // You'd think we could just set the PATH, but there be dragons here - // https://github.com/palantir/gradle-docker/issues/162 - def exe = exePresent('iscc') ? 'iscc' : file('ci/iscc') - def iss = layout.buildDirectory.file("innosetup/${args.name}/OmegaT.iss").get().asFile - logging.captureStandardOutput LogLevel.INFO - commandLine exe, '/Qp', iss - outputs.file layout.buildDirectory.file("innosetup/${args.name}/${installerBasename}.exe") - doLast { - println "" - def f3 = layout.buildDirectory.file("innosetup/${args.name}/${installerBasename}.exe").get().asFile - logger.info('Built ' + f3.toString() + "(" + f3.length() + ")") - } - } - - tasks.register(distsTaskName, Copy) { - description = "Create a Windows installer for ${args.name} distro. " + - 'Requires Inno Setup (http://www.jrsoftware.org/isinfo.php).' - onlyIf { - conditions([!args.jrePath || !args.jrePath.empty, 'JRE not found'], - [exePresent('iscc') || exePresent('docker') || exePresent('nerdctl'), - 'InnoSetup or Docker not installed']) - } - from layout.buildDirectory.file("innosetup/${args.name}/${installerBasename}.exe") - into base.distsDirectory - outputs.file installerWinExe - dependsOn genDistsTaskName - } - - tasks.register(signedTaskName, Exec) { - group = 'omegat distribution' - inputs.file installerWinExe skipWhenEmpty() - outputs.file signedWinExe - // Starting from Nov 2022, certification provider force to use HSM to - // store private keys. Starting on June 1, 2023, at 00:00 UTC, industry - // standards will require private keys for standard code signing - // certificates to be stored on hardware certified as FIPS 140 Level 2, - // Common Criteria EAL 4+, or equivalent. - // #1179 build/release: Windows binary signature with PKCS#11 HSM - // https://sourceforge.net/p/omegat/bugs/1179/ - // requires osslsigncode version 2.5 or later. - onlyIf { - // Set these in e.g. local.properties - def props = ['pkcs11module', 'winCodesignPassword'] - conditions([props.every { project.hasProperty(it) }, 'Code signing properties not set'], - [exePresent('osslsigncode'), 'osslsigncode is not installed']) - // note: container image amake/innosetup has osslsigntool 1.7 which don't work. - // [exePresent('osslsigncode') || exePresent('docker') || exePresent('nerdctl'), - // 'neither osslsigncode or docker/nerdctl is not installed']) - } - doFirst { - delete signedWinExe - } - def envVars = [:] - commandLine(signedTaskCommandArgs()) - environment(envVars) - dependsOn distsTaskName - } - assemble.dependsOn args.name, signedTaskName - win.dependsOn args.name, signedTaskName -} -makeWinTask(name: 'winNoJRE', suffix: 'Windows_without_JRE') -makeWinTask(name: 'winJRE64', suffix: 'Windows_64', jrePath: windowsJRE, arch: 'x64') -makeWinTask(name: 'winJRE', suffix: 'Windows', jrePath: windowsJRE32) - // Disable .tar distributions for everyone but Linux tasks.findAll { it.name =~ /[dD]istTar$/ && !it.name.contains('linux') }.each { it.enabled = false } // Disable .zip distributions for Linux @@ -1453,51 +805,6 @@ tasks.register('checksums') { } } -tasks.register('genJAXB') { - description = 'Generate classes for loading and manipulating XML formats' -} - -ext.makeJaxbTask = { args -> - def taskName = "gen${args.name.capitalize()}" - tasks.register(taskName, JavaExec) { - classpath = configurations.jaxb - mainClass = 'com.sun.tools.xjc.XJCFacade' - delegate.args args.args - outputs.dir args.outdir - } - genJAXB.dependsOn taskName -} - -makeJaxbTask(name: 'segmentation', outdir: 'src/gen/core/segmentation', - args: ['-no-header', '-d', 'src', '-p', 'gen.core.segmentation', 'src/schemas/srx20.xsd']) -makeJaxbTask(name: 'filters', outdir: 'src/gen/core/filters', - args: ['-no-header', '-d', 'src', '-p', 'gen.core.filters', 'src/schemas/filters.xsd']) -makeJaxbTask(name: 'tbx', outdir: 'src/gen/core/tbx', - args: ['-no-header', '-d', 'src', '-p', 'gen.core.tbx', 'src/schemas/tbx.xsd']) -makeJaxbTask(name: 'project', outdir: 'src/gen/core/project', - args: ['-no-header', '-d', 'src', '-p', 'gen.core.project', 'src/schemas/project_properties.xsd']) -makeJaxbTask(name: 'tmx14', outdir: 'src/gen/core/tmx14', - args: ['-no-header', '-d', 'src', '-p', 'gen.core.tmx14', '-b', 'src/schemas/tmx14.xjb', 'src/schemas/tmx14.xsd']) - -tasks.register('changedOnBranch') { - description = 'List files that have been modified on this git branch.' - doLast { - ext.files = project.files(gitModifiedFiles()) - ext.files.each { println(it) } - } -} - -tasks.register('spotlessChangedApply') { - description = 'Apply code formatting to files that have been changed on the current branch.' - group = 'omegat workflow' - finalizedBy 'spotlessApply' - dependsOn changedOnBranch - doFirst { - spotlessJava.target = changedOnBranch.files.findAll { - it.path.endsWith('.java') - } - } -} jacoco { toolVersion = "0.8.12" @@ -1557,7 +864,7 @@ tasks.register('debug', JavaExec) { mainClass = application.mainClass classpath = sourceSets.main.runtimeClasspath jvmArgs(["--illegal-access=warn"]) - debug true + debug = true } tasks.register('runOnJava17', JavaExec) { @@ -1587,16 +894,6 @@ tasks.register('runOnJava21', JavaExec) { // E.g. when doing `build`, run checks before making distfiles assemble.mustRunAfter check -test { - // Test in headless mode with ./gradlew test -Pheadless - if (project.hasProperty('headless')) { - systemProperty 'java.awt.headless', 'true' - } - systemProperty 'java.util.logging.config.file', "${rootDir}/config/test/logger.properties" - // some test case depends on modules from subproject - dependsOn subprojects.collect { it.tasks.named('jar') } -} - tasks.register('testIntegration', JavaExec) { description = 'Run integration tests. Pass repo URL as -Domegat.test.repo=' javaLauncher = javaToolchains.launcherFor { @@ -1638,44 +935,21 @@ tasks.register('testOnJava21', Test) { group = 'verification' } -def startX(display) { - def outputStream = new ByteArrayOutputStream() - exec { - commandLine 'sh', '-c', "Xvfb :${display} -screen 0 1280x1024x24 >>/dev/null 2>&1 & echo \$!" - standardOutput = outputStream - } - def xvfbPid = outputStream.toString().trim() - println "Virtual X server is started with DISPLAY :${display} and PID: ${xvfbPid}" - return xvfbPid -} - -def stopX(pid) { - println "Stopping virtual X server..." - def outputStream = new ByteArrayOutputStream() - exec { - commandLine 'sh', '-c', "kill ${pid} &" - standardOutput = outputStream - errorOutput = outputStream - } -} - -def isCommandAvailable(command) { - def outputStream = new ByteArrayOutputStream() - exec { - commandLine 'sh', '-c', "command -v ${command}" - ignoreExitValue true - standardOutput = outputStream - errorOutput = outputStream - }.exitValue == 0 -} - project.ext.xvfbPid = "" def testAcceptanceFinally = tasks.register('testAcceptanceFinally') { outputs.upToDateWhen { return false } // always run doLast { if (!project.ext.xvfbPid.isEmpty()) { - stopX(project.ext.xvfbPid) + println "Stopping virtual X server..." + def outputStream = new ByteArrayOutputStream() + def injected = project.objects.newInstance(InjectedExecOps) + def pid = project.ext.xvfbPid + injected.execOps.exec { + commandLine 'sh', '-c', "kill ${pid} &" + standardOutput = outputStream + errorOutput = outputStream + } project.ext.xfvbPid = "" } } @@ -1686,8 +960,17 @@ tasks.register('testAcceptance', Test) { if (!display.isEmpty()) { def lockFile = new File("/tmp/.X${display}-lock") if (!lockFile.exists()) { - project.ext.xvfbPid = startX(display) - environment 'DISPLAY', ":${display}" + def outputStream = new ByteArrayOutputStream() + def injected = project.objects.newInstance(InjectedExecOps) + def res = injected.execOps.exec { + commandLine 'sh', '-c', "Xvfb :${display} -screen 0 1280x1024x24 >>/dev/null 2>&1 & echo \$!" + standardOutput = outputStream + }.exitValue + if (res == 0) { + def xvfbPid = outputStream.toString().trim() + environment 'DISPLAY', ":${display}" + println "Virtual X server is started with DISPLAY :${display} and PID: ${xvfbPid}" + } } } finalizedBy testAcceptanceFinally @@ -1704,7 +987,7 @@ tasks.register('testAcceptance', Test) { classpath = sourceSets.testAcceptance.runtimeClasspath systemProperties = System.properties systemProperty 'java.util.logging.config.file', "${rootDir}/config/test/logger.properties" - dependsOn firstStepsEn + dependsOn tasks.findByName('firstStepsEn') dependsOn ':aligner:jar' } @@ -1747,8 +1030,8 @@ publishing { maven { url = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/' credentials(PasswordCredentials) { - username findProperty('ossrhUsername') - password findProperty('ossrhPassword') + username = findProperty('ossrhUsername') + password = findProperty('ossrhPassword') } } } @@ -1822,20 +1105,6 @@ tasks.withType(JavaExec).configureEach { // `gradle -PrunMaxHeapSize=1024M run` maxHeapSize = findProperty('runMaxHeapSize') // Ask modules to be up-to-date before run task executed - dependsOn subprojects.tasks.jar - dependsOn firstSteps -} - -tasks.register('printVersion') { - println project.version -} - -tasks.withType(Copy).configureEach { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} -tasks.withType(Sync).configureEach { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE -} -tasks.withType(Zip).configureEach { - duplicatesStrategy = DuplicatesStrategy.INCLUDE + dependsOn subprojects.collect {it.tasks.withType(Jar)} + dependsOn tasks.findByName('firstSteps') } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..cb9f734351 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +// OmegaT distribution package meta data. +shortDescription = 'The free translation memory tool' +distDescription = 'OmegaT is a free and open source multiplatform Computer Assisted Translation tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects.' +distAppVendor = 'The OmegaT project' +omtWebsite = 'https://omegat.org' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 879256d4f5..b4e63fe328 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -186,8 +186,5 @@ dictionary = ["trie4j", "dsl4j", "stardict4j", "jsoup"] xmlunit = ["xmlunit-core", "xmlunit-assertj", "assertj"] [plugins] -spotbugs = {id = "com.github.spotbugs", version = "6.0.26"} -spotless = {id = "com.diffplug.spotless", version = "7.0.0.BETA4"} launch4j = {id = "edu.sc.seis.launch4j", version = "3.0.6"} -versions = {id = "com.github.ben-manes.versions", version = "0.51.0"} ssh = {id = "org.hidetake.ssh", version = "2.11.2"} diff --git a/gradle/utils.gradle b/gradle/utils.gradle deleted file mode 100644 index 2b805f3d58..0000000000 --- a/gradle/utils.gradle +++ /dev/null @@ -1,117 +0,0 @@ -ext { - loadProperties = { propFile -> - def config = new Properties() - if (propFile.canRead()) { - propFile.withInputStream { config.load(it) } - } - config - } - - getExecOutput = { ... args = [] -> - def exe = providers.exec { - ignoreExitValue = true - commandLine args - } - exe.result.get() == 0 ? exe.standardOutput.asText.get().trim() : '' - } - - getUpdateSuffix = { update -> - if (!update || update == '0') { - return '' - } - if (update.length() == 1) { - return "_0${update}" - } - "_${update}" - } - - gitBranch = { - providers.exec {commandLine("git", "name-rev", "--name-only", "HEAD")}.standardOutput.asText.get().trim() - } - - gitRemote = { - def branch = gitBranch() - def exe = providers.exec { - ignoreExitValue = true - commandLine("git", "config", "branch.${branch}.pushRemote") - } - def remote = exe.result.get() == 0 ? exe.standardOutput.asText.get().trim() : '' - if (remote.empty) { - exe = providers.exec { - ignoreExitValue = true - commandLine("git", "config", "branch.${branch}.remote") - } - remote = exe.result.get() == 0 ? exe.standardOutput.asText.get().trim() : '' - } - if (!remote.empty) { - remote - } else { - logger.warn("Could not detect git remote name. Assuming 'origin'.") - 'origin' - } - } - - gitBranchSplitPoint = { - def remote = gitRemote() - if (!remote.empty) { - providers.exec { - commandLine("git", "merge-base", "HEAD", "$remote/master") - }.standardOutput.asText.get().trim() - } else { - '' - } - } - - gitModifiedFiles = { - def splitPoint = gitBranchSplitPoint() - if (!splitPoint.empty) { - providers.exec { - commandLine("git", "diff", "--name-only", "HEAD", "$splitPoint") - }.standardOutput.asText.get().trim().tokenize() - } else { - [] - } - } - - exePresent = { exe -> - ["where $exe", "which $exe"].any { - try { - def findExe = it.execute() - findExe.waitForProcessOutput() - return findExe.exitValue() == 0 - } catch (any) { - return false - } - } - } - - conditions = { List... items -> - items.each { val, str -> - if (!val) { - logger.warn(str) - } - } - items.every { it[0] } - } - - condition = { val, str -> - conditions([val, str]) - } - - replaceRelativePathSegment = { FileCopyDetails deets, pattern, replacement -> - def segs = deets.relativePath.segments.collect { - it =~ pattern ? replacement : it - } - deets.relativePath = new RelativePath(!deets.directory, segs as String[]) - } - - camelCase = { str -> - str.tokenize('-_ ').withIndex().collect { tok, i -> - i == 0 ? tok : tok.capitalize() - }.join() - } - - clsName = { cls -> - cls.split('\\.').last() - } -} diff --git a/language-modules/ar/build.gradle b/language-modules/ar/build.gradle index 691ac7f797..b839ffabcb 100644 --- a/language-modules/ar/build.gradle +++ b/language-modules/ar/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/ast/build.gradle b/language-modules/ast/build.gradle index 9aba6766a1..112978b134 100644 --- a/language-modules/ast/build.gradle +++ b/language-modules/ast/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } @@ -33,7 +33,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/be/build.gradle b/language-modules/be/build.gradle index 126753b04a..251d67a564 100644 --- a/language-modules/be/build.gradle +++ b/language-modules/be/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/br/build.gradle b/language-modules/br/build.gradle index 15362ba1ff..f4b3f1ed89 100644 --- a/language-modules/br/build.gradle +++ b/language-modules/br/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/ca/build.gradle b/language-modules/ca/build.gradle index a3432ec2e2..b59f18e961 100644 --- a/language-modules/ca/build.gradle +++ b/language-modules/ca/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/da/build.gradle b/language-modules/da/build.gradle index 0c2085b249..8b1dfea410 100644 --- a/language-modules/da/build.gradle +++ b/language-modules/da/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/de/build.gradle b/language-modules/de/build.gradle index 0be511767f..7ffcaea4d7 100644 --- a/language-modules/de/build.gradle +++ b/language-modules/de/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -37,8 +37,8 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/el/build.gradle b/language-modules/el/build.gradle index 565cc589ae..5b325ce32a 100644 --- a/language-modules/el/build.gradle +++ b/language-modules/el/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,8 +34,8 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/en/build.gradle b/language-modules/en/build.gradle index 12722ef059..8e2e938e22 100644 --- a/language-modules/en/build.gradle +++ b/language-modules/en/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -32,7 +32,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/eo/build.gradle b/language-modules/eo/build.gradle index 280b1533a6..ae7fe40b14 100644 --- a/language-modules/eo/build.gradle +++ b/language-modules/eo/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -32,7 +32,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/es/build.gradle b/language-modules/es/build.gradle index 04f5f3c762..cdaa11174b 100644 --- a/language-modules/es/build.gradle +++ b/language-modules/es/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,8 +34,8 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/fa/build.gradle b/language-modules/fa/build.gradle index 83f9e53658..d887495c53 100644 --- a/language-modules/fa/build.gradle +++ b/language-modules/fa/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/fr/build.gradle b/language-modules/fr/build.gradle index 8ed0f28c3b..08bc908248 100644 --- a/language-modules/fr/build.gradle +++ b/language-modules/fr/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/ga/build.gradle b/language-modules/ga/build.gradle index d9ca9cdd36..67a4e127ec 100644 --- a/language-modules/ga/build.gradle +++ b/language-modules/ga/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/gl/build.gradle b/language-modules/gl/build.gradle index 1da90d36ac..7d0081cfa1 100644 --- a/language-modules/gl/build.gradle +++ b/language-modules/gl/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/it/build.gradle b/language-modules/it/build.gradle index d268b8d718..23957a9bb6 100644 --- a/language-modules/it/build.gradle +++ b/language-modules/it/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/ja/build.gradle b/language-modules/ja/build.gradle index 4a1deeda4d..4f28b647a8 100644 --- a/language-modules/ja/build.gradle +++ b/language-modules/ja/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/language-modules/km/build.gradle b/language-modules/km/build.gradle index 242ed5d265..b5358073ca 100644 --- a/language-modules/km/build.gradle +++ b/language-modules/km/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/nl/build.gradle b/language-modules/nl/build.gradle index f9955f23fe..5fd058807f 100644 --- a/language-modules/nl/build.gradle +++ b/language-modules/nl/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/pl/build.gradle b/language-modules/pl/build.gradle index 5e540af6b4..490b814e09 100644 --- a/language-modules/pl/build.gradle +++ b/language-modules/pl/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/pt/build.gradle b/language-modules/pt/build.gradle index 2aaedb9009..6d196dcf20 100644 --- a/language-modules/pt/build.gradle +++ b/language-modules/pt/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/ro/build.gradle b/language-modules/ro/build.gradle index 5d741fd56a..9d958dcb7b 100644 --- a/language-modules/ro/build.gradle +++ b/language-modules/ro/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/ru/build.gradle b/language-modules/ru/build.gradle index 2aa61296c2..631b479440 100644 --- a/language-modules/ru/build.gradle +++ b/language-modules/ru/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/sk/build.gradle b/language-modules/sk/build.gradle index a3149691a2..0d20b031e1 100644 --- a/language-modules/sk/build.gradle +++ b/language-modules/sk/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/sl/build.gradle b/language-modules/sl/build.gradle index 05b5682154..06e850b20d 100644 --- a/language-modules/sl/build.gradle +++ b/language-modules/sl/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/sv/build.gradle b/language-modules/sv/build.gradle index 22d561c7d5..ea757894f3 100644 --- a/language-modules/sv/build.gradle +++ b/language-modules/sv/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/ta/build.gradle b/language-modules/ta/build.gradle index a143a06890..b4fe1bd397 100644 --- a/language-modules/ta/build.gradle +++ b/language-modules/ta/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/tl/build.gradle b/language-modules/tl/build.gradle index aacf06a96e..fda1003d8e 100644 --- a/language-modules/tl/build.gradle +++ b/language-modules/tl/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -33,7 +33,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:morfologik").tasks.jar + dependsOn project(":spellchecker:morfologik").tasks.withType(Jar) } jar { diff --git a/language-modules/uk/build.gradle b/language-modules/uk/build.gradle index 9d439d24b4..750e95290a 100644 --- a/language-modules/uk/build.gradle +++ b/language-modules/uk/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { @@ -34,7 +34,7 @@ dependencies { test { dependsOn tasks.jar - dependsOn project(":spellchecker:hunspell").tasks.jar + dependsOn project(":spellchecker:hunspell").tasks.withType(Jar) } jar { diff --git a/language-modules/zh/build.gradle b/language-modules/zh/build.gradle index 226985b978..8821fd79dd 100644 --- a/language-modules/zh/build.gradle +++ b/language-modules/zh/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/apertium/build.gradle b/machinetranslators/apertium/build.gradle index a24feb2af5..c961473c6a 100644 --- a/machinetranslators/apertium/build.gradle +++ b/machinetranslators/apertium/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/belazar/build.gradle b/machinetranslators/belazar/build.gradle index f2863ac962..6aab862d66 100644 --- a/machinetranslators/belazar/build.gradle +++ b/machinetranslators/belazar/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/deepl/build.gradle b/machinetranslators/deepl/build.gradle index 301cdee739..ea7be24895 100644 --- a/machinetranslators/deepl/build.gradle +++ b/machinetranslators/deepl/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/dummy/build.gradle b/machinetranslators/dummy/build.gradle index 2be6b163d7..f8125749de 100644 --- a/machinetranslators/dummy/build.gradle +++ b/machinetranslators/dummy/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/google/build.gradle b/machinetranslators/google/build.gradle index d726044ea7..43b1283f98 100644 --- a/machinetranslators/google/build.gradle +++ b/machinetranslators/google/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/ibmwatson/build.gradle b/machinetranslators/ibmwatson/build.gradle index 141dc45e16..ff36d560e5 100644 --- a/machinetranslators/ibmwatson/build.gradle +++ b/machinetranslators/ibmwatson/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/mymemory/build.gradle b/machinetranslators/mymemory/build.gradle index a93f5a0666..8f1a7f8fd4 100644 --- a/machinetranslators/mymemory/build.gradle +++ b/machinetranslators/mymemory/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/machinetranslators/yandex/build.gradle b/machinetranslators/yandex/build.gradle index 5602e48f2f..0c9be4eac5 100644 --- a/machinetranslators/yandex/build.gradle +++ b/machinetranslators/yandex/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/settings.gradle b/settings.gradle index 81719fd28f..2ccaaade58 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,8 @@ +pluginManagement { + // Include 'plugins build' to define convention plugins. + includeBuild("build-logic") +} + plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' id 'com.gradle.develocity' version '3.19' diff --git a/spellchecker/hunspell/build.gradle b/spellchecker/hunspell/build.gradle index 262ea83369..38042e6fb3 100644 --- a/spellchecker/hunspell/build.gradle +++ b/spellchecker/hunspell/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/spellchecker/morfologik/build.gradle b/spellchecker/morfologik/build.gradle index f4ff3acf4b..b7cb385122 100644 --- a/spellchecker/morfologik/build.gradle +++ b/spellchecker/morfologik/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/test/fixtures/org/omegat/util/CommonVerifications.java b/test/fixtures/org/omegat/util/CommonVerifications.java index 6ee2e8281b..b6e06362d4 100644 --- a/test/fixtures/org/omegat/util/CommonVerifications.java +++ b/test/fixtures/org/omegat/util/CommonVerifications.java @@ -46,8 +46,6 @@ import java.util.regex.Pattern; import java.util.stream.Stream; -import org.omegat.Main; - public class CommonVerifications { /** @@ -67,7 +65,7 @@ protected void assertBundle(String basename) throws IOException { } protected void assertEncoding(String bundle) throws IOException { - try (InputStream stream = Main.class.getResourceAsStream(bundle)) { + try (InputStream stream = getClass().getResourceAsStream(bundle)) { if (stream == null) { return; } diff --git a/theme/build.gradle b/theme/build.gradle index bbe1d6de77..38c7a79c44 100644 --- a/theme/build.gradle +++ b/theme/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } dependencies { diff --git a/tipoftheday/build.gradle b/tipoftheday/build.gradle index f85b9d1bb4..91888643fc 100644 --- a/tipoftheday/build.gradle +++ b/tipoftheday/build.gradle @@ -3,7 +3,7 @@ */ plugins { - id 'java-library' + id 'org.omegat.java-conventions' } sourceSets {