Skip to content

Commit 8a46bfd

Browse files
committed
refactor(Gradle): Move top-level functions to utility objects
This is a prerequisite for leveraging Gradle's configuration cache which cannot handle top-level functions [1]. [1]: https://docs.gradle.org/8.2/userguide/configuration_cache.html#config_cache:not_yet_implemented:accessing_top_level_at_execution Signed-off-by: Sebastian Schuberth <[email protected]>
1 parent 855440d commit 8a46bfd

File tree

2 files changed

+151
-126
lines changed

2 files changed

+151
-126
lines changed

build.gradle.kts

Lines changed: 10 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -89,83 +89,9 @@ tasks.register("allDependencies") {
8989
}
9090
}
9191

92-
val copyrightExcludedPaths = listOf(
93-
"LICENSE",
94-
"NOTICE",
95-
"batect",
96-
"gradlew",
97-
"gradle/",
98-
"examples/",
99-
"integrations/completions/",
100-
"plugins/reporters/asciidoc/src/main/resources/pdf-theme/pdf-theme.yml",
101-
"plugins/reporters/asciidoc/src/main/resources/templates/freemarker_implicit.ftl",
102-
"plugins/reporters/fossid/src/main/resources/templates/freemarker_implicit.ftl",
103-
"plugins/reporters/freemarker/src/main/resources/templates/freemarker_implicit.ftl",
104-
"plugins/reporters/static-html/src/main/resources/prismjs/",
105-
"plugins/reporters/web-app-template/yarn.lock",
106-
"resources/META-INF/",
107-
"resources/exceptions/",
108-
"resources/licenses/",
109-
"resources/licenserefs/",
110-
"test/assets/",
111-
"funTest/assets/"
112-
)
113-
114-
val copyrightExcludedExtensions = listOf(
115-
"css",
116-
"graphql",
117-
"json",
118-
"md",
119-
"png",
120-
"svg",
121-
"ttf"
122-
)
123-
124-
fun getCopyrightableFiles(rootDir: File): List<File> {
125-
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
126-
127-
return gitFilesProvider.get().filter { file ->
128-
val isHidden = file.toPath().any { it.toString().startsWith(".") }
129-
130-
!isHidden
131-
&& copyrightExcludedPaths.none { it in file.invariantSeparatorsPath }
132-
&& file.extension !in copyrightExcludedExtensions
133-
}
134-
}
135-
136-
val maxCopyrightLines = 50
137-
138-
fun extractCopyrights(file: File): List<String> {
139-
val copyrights = mutableListOf<String>()
140-
141-
var lineCounter = 0
142-
143-
file.useLines { lines ->
144-
lines.forEach { line ->
145-
if (++lineCounter > maxCopyrightLines) return@forEach
146-
val copyright = line.replaceBefore(" Copyright ", "", "").trim()
147-
if (copyright.isNotEmpty() && !copyright.endsWith("\"")) copyrights += copyright
148-
}
149-
}
150-
151-
return copyrights
152-
}
153-
154-
val copyrightPrefixRegex = Regex("Copyright .*\\d{2,}(-\\d{2,})? ", RegexOption.IGNORE_CASE)
155-
156-
fun extractCopyrightHolders(statements: Collection<String>): List<String> {
157-
val holders = mutableListOf<String>()
158-
159-
statements.mapNotNullTo(holders) { statement ->
160-
val holder = statement.replace(copyrightPrefixRegex, "")
161-
holder.takeUnless { it == statement }?.trim()
162-
}
163-
164-
return holders
165-
}
166-
16792
val checkCopyrightsInNoticeFile by tasks.registering {
168-
val files = getCopyrightableFiles(rootDir)
93+
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
94+
val files = CopyrightableFiles.filter(gitFilesProvider)
16995
val noticeFile = rootDir.resolve("NOTICE")
17096
val genericHolderPrefix = "The ORT Project Authors"
17197

@@ -176,7 +102,7 @@ val checkCopyrightsInNoticeFile by tasks.registering {
176102
var hasViolations = false
177103

178104
files.forEach { file ->
179-
val copyrights = extractCopyrights(file)
105+
val copyrights = CopyrightUtils.extract(file)
180106
if (copyrights.isNotEmpty()) {
181107
allCopyrights += copyrights
182108
} else {
@@ -186,7 +112,7 @@ val checkCopyrightsInNoticeFile by tasks.registering {
186112
}
187113

188114
val notices = noticeFile.readLines()
189-
extractCopyrightHolders(allCopyrights).forEach { holder ->
115+
CopyrightUtils.extractHolders(allCopyrights).forEach { holder ->
190116
if (!holder.startsWith(genericHolderPrefix) && notices.none { holder in it }) {
191117
hasViolations = true
192118
logger.error("The '$holder' Copyright holder is not captured in '$noticeFile'.")
@@ -197,51 +123,9 @@ val checkCopyrightsInNoticeFile by tasks.registering {
197123
}
198124
}
199125

200-
val lastLicenseHeaderLine = "License-Filename: LICENSE"
201-
202-
val expectedCopyrightHolder =
203-
"The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)"
204-
205-
// The header without `lastLicenseHeaderLine` as that line is used as a marker.
206-
val expectedLicenseHeader = """
207-
Licensed under the Apache License, Version 2.0 (the "License");
208-
you may not use this file except in compliance with the License.
209-
You may obtain a copy of the License at
210-
211-
https://www.apache.org/licenses/LICENSE-2.0
212-
213-
Unless required by applicable law or agreed to in writing, software
214-
distributed under the License is distributed on an "AS IS" BASIS,
215-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
216-
See the License for the specific language governing permissions and
217-
limitations under the License.
218-
219-
SPDX-License-Identifier: Apache-2.0
220-
""".trimIndent()
221-
222-
fun extractLicenseHeader(file: File): List<String> {
223-
var headerLines = file.useLines { lines ->
224-
lines.takeWhile { !it.endsWith(lastLicenseHeaderLine) }.toList()
225-
}
226-
227-
while (true) {
228-
val uniqueColumnChars = headerLines.mapNotNullTo(mutableSetOf()) { it.firstOrNull() }
229-
230-
// If there are very few different chars in a column, assume that column to consist of comment chars /
231-
// indentation only.
232-
if (uniqueColumnChars.size < 3) {
233-
val trimmedHeaderLines = headerLines.mapTo(mutableListOf()) { it.drop(1) }
234-
headerLines = trimmedHeaderLines
235-
} else {
236-
break
237-
}
238-
}
239-
240-
return headerLines
241-
}
242-
243126
val checkLicenseHeaders by tasks.registering {
244-
val files = getCopyrightableFiles(rootDir)
127+
val gitFilesProvider = providers.of(GitFilesValueSource::class) { parameters { workingDir = rootDir } }
128+
val files = CopyrightableFiles.filter(gitFilesProvider)
245129

246130
inputs.files(files)
247131

@@ -252,15 +136,15 @@ val checkLicenseHeaders by tasks.registering {
252136
var hasErrors = false
253137

254138
files.forEach { file ->
255-
val headerLines = extractLicenseHeader(file)
139+
val headerLines = LicenseUtils.extractHeader(file)
256140

257-
val holders = extractCopyrightHolders(headerLines)
258-
if (holders.singleOrNull() != expectedCopyrightHolder) {
141+
val holders = CopyrightUtils.extractHolders(headerLines)
142+
if (holders.singleOrNull() != CopyrightUtils.expectedHolder) {
259143
hasErrors = true
260144
logger.error("Unexpected copyright holder(s) in file '$file': $holders")
261145
}
262146

263-
if (!headerLines.joinToString("\n").endsWith(expectedLicenseHeader)) {
147+
if (!headerLines.joinToString("\n").endsWith(LicenseUtils.expectedHeader)) {
264148
hasErrors = true
265149
logger.error("Unexpected license header in file '$file'.")
266150
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright (C) 2023 The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
import java.io.File
21+
22+
import org.gradle.api.provider.Provider
23+
24+
object CopyrightableFiles {
25+
private val excludedPaths = listOf(
26+
"LICENSE",
27+
"NOTICE",
28+
"batect",
29+
"gradlew",
30+
"gradle/",
31+
"examples/",
32+
"integrations/completions/",
33+
"plugins/reporters/asciidoc/src/main/resources/pdf-theme/pdf-theme.yml",
34+
"plugins/reporters/asciidoc/src/main/resources/templates/freemarker_implicit.ftl",
35+
"plugins/reporters/fossid/src/main/resources/templates/freemarker_implicit.ftl",
36+
"plugins/reporters/freemarker/src/main/resources/templates/freemarker_implicit.ftl",
37+
"plugins/reporters/static-html/src/main/resources/prismjs/",
38+
"plugins/reporters/web-app-template/yarn.lock",
39+
"resources/META-INF/",
40+
"resources/exceptions/",
41+
"resources/licenses/",
42+
"resources/licenserefs/",
43+
"test/assets/",
44+
"funTest/assets/"
45+
)
46+
47+
private val excludedExtensions = listOf(
48+
"css",
49+
"graphql",
50+
"json",
51+
"md",
52+
"png",
53+
"svg",
54+
"ttf"
55+
)
56+
57+
fun filter(filesProvider: Provider<List<File>>): List<File> = filesProvider.get().filter { file ->
58+
val isHidden = file.toPath().any { it.toString().startsWith(".") }
59+
60+
!isHidden
61+
&& excludedPaths.none { it in file.invariantSeparatorsPath }
62+
&& file.extension !in excludedExtensions
63+
}
64+
}
65+
66+
object CopyrightUtils {
67+
const val expectedHolder =
68+
"The ORT Project Authors (see <https://github.com/oss-review-toolkit/ort/blob/main/NOTICE>)"
69+
70+
private const val maxCopyrightLines = 50
71+
private val copyrightPrefixRegex = Regex("Copyright .*\\d{2,}(-\\d{2,})? ", RegexOption.IGNORE_CASE)
72+
73+
fun extract(file: File): List<String> {
74+
val copyrights = mutableListOf<String>()
75+
76+
var lineCounter = 0
77+
78+
file.useLines { lines ->
79+
lines.forEach { line ->
80+
if (++lineCounter > maxCopyrightLines) return@forEach
81+
val copyright = line.replaceBefore(" Copyright ", "", "").trim()
82+
if (copyright.isNotEmpty() && !copyright.endsWith("\"")) copyrights += copyright
83+
}
84+
}
85+
86+
return copyrights
87+
}
88+
89+
fun extractHolders(statements: Collection<String>): List<String> {
90+
val holders = mutableListOf<String>()
91+
92+
statements.mapNotNullTo(holders) { statement ->
93+
val holder = statement.replace(copyrightPrefixRegex, "")
94+
holder.takeUnless { it == statement }?.trim()
95+
}
96+
97+
return holders
98+
}
99+
}
100+
101+
object LicenseUtils {
102+
// The header without `lastHeaderLine` as that line is used as a marker.
103+
val expectedHeader = """
104+
Licensed under the Apache License, Version 2.0 (the "License");
105+
you may not use this file except in compliance with the License.
106+
You may obtain a copy of the License at
107+
108+
https://www.apache.org/licenses/LICENSE-2.0
109+
110+
Unless required by applicable law or agreed to in writing, software
111+
distributed under the License is distributed on an "AS IS" BASIS,
112+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
113+
See the License for the specific language governing permissions and
114+
limitations under the License.
115+
116+
SPDX-License-Identifier: Apache-2.0
117+
""".trimIndent()
118+
119+
private const val lastHeaderLine = "License-Filename: LICENSE"
120+
121+
fun extractHeader(file: File): List<String> {
122+
var headerLines = file.useLines { lines ->
123+
lines.takeWhile { !it.endsWith(lastHeaderLine) }.toList()
124+
}
125+
126+
while (true) {
127+
val uniqueColumnChars = headerLines.mapNotNullTo(mutableSetOf()) { it.firstOrNull() }
128+
129+
// If there are very few different chars in a column, assume that column to consist of comment chars /
130+
// indentation only.
131+
if (uniqueColumnChars.size < 3) {
132+
val trimmedHeaderLines = headerLines.mapTo(mutableListOf()) { it.drop(1) }
133+
headerLines = trimmedHeaderLines
134+
} else {
135+
break
136+
}
137+
}
138+
139+
return headerLines
140+
}
141+
}

0 commit comments

Comments
 (0)