mirror of https://github.com/weisJ/darklaf.git
Browse Source
Migrate to Nokee plugin. Improved dark mode detection on macOS. Fixed issue where the native theme option wasn't available in the theme settings on macOS. Improved scrollbars.pull/188/head
Jannis Weis
5 years ago
committed by
GitHub
45 changed files with 957 additions and 313 deletions
@ -1,16 +1,28 @@
|
||||
plugins { |
||||
`kotlin-dsl` |
||||
`java-gradle-plugin` |
||||
groovy |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(kotlin("gradle-plugin")) |
||||
implementation(platform("dev.nokee:nokee-gradle-plugins:0.4.0-60cff2b")) |
||||
} |
||||
|
||||
repositories { |
||||
mavenCentral() |
||||
gradlePluginPortal() |
||||
maven { url = uri("https://dl.bintray.com/nokeedev/distributions") } |
||||
maven { url = uri("https://dl.bintray.com/nokeedev/distributions-snapshots") } |
||||
} |
||||
|
||||
configure<KotlinDslPluginOptions> { |
||||
experimentalWarning.set(false) |
||||
gradlePlugin { |
||||
plugins { |
||||
create("uber-jni-jar") { |
||||
id = "uber-jni-jar" |
||||
implementationClass = "UberJniJarPlugin" |
||||
} |
||||
create("use-prebuilt-binaries") { |
||||
id = "use-prebuilt-binaries" |
||||
implementationClass = "UsePrebuiltBinariesWhenUnbuildablePlugin" |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,14 @@
|
||||
import java.util.concurrent.Callable |
||||
|
||||
class CallableLogger extends OneTimeLogger implements Callable<List<File>> { |
||||
|
||||
CallableLogger(Runnable logger) { |
||||
super(logger) |
||||
} |
||||
|
||||
@Override |
||||
List<File> call() throws Exception { |
||||
super.log() |
||||
return Collections.emptyList() |
||||
} |
||||
} |
@ -0,0 +1,351 @@
|
||||
import groovy.json.JsonOutput |
||||
import groovy.json.JsonSlurper |
||||
import groovy.transform.CompileStatic |
||||
import org.gradle.api.DefaultTask |
||||
import org.gradle.api.GradleException |
||||
import org.gradle.api.Transformer |
||||
import org.gradle.api.tasks.OutputFile |
||||
|
||||
import java.nio.file.Files |
||||
import java.nio.file.Path |
||||
import java.nio.file.StandardCopyOption |
||||
import java.util.concurrent.locks.ReadWriteLock |
||||
import java.util.concurrent.locks.ReentrantReadWriteLock |
||||
import java.util.stream.Stream |
||||
import java.util.zip.ZipEntry |
||||
import java.util.zip.ZipFile |
||||
|
||||
@CompileStatic |
||||
class DownloadPrebuiltBinaryFromGitHubAction extends DefaultTask { |
||||
|
||||
private static final ReadWriteLock LOCK = new ReentrantReadWriteLock() |
||||
|
||||
private static final String VERSION_INFO_FILE_NAME = "github_artifact_versions.json" |
||||
private static final String TEMP_PATH = "tmp${File.separator}prebuild" |
||||
private static final String PRE_BUILD_PATH = "libs${File.separator}prebuild" |
||||
|
||||
private final OneTimeLogger tokenWarning = new OneTimeLogger.Static({ |
||||
error("""No github access token is specified. Latest artifacts will need to be included manually. |
||||
|The access token needs to have the 'read-public' property. Specify using: |
||||
| -PgithubAccessToken=<your token> |
||||
|or by setting |
||||
| githubAccessToken=<your token> |
||||
|inside the gradle.properties file. |
||||
|""".stripMargin()) |
||||
}, "missingTokenWarning") |
||||
private final OneTimeLogger useCachedWarning = new OneTimeLogger({ |
||||
log("Could not download artifact or artifact information. Using cached version") |
||||
}) |
||||
|
||||
private Map cacheInfo |
||||
|
||||
private String manualDownloadUrl = "" |
||||
private String user |
||||
private String repository |
||||
private String workflow |
||||
private List<String> branches = [] |
||||
private boolean missingLibraryIsFailure |
||||
|
||||
private String githubAccessToken |
||||
private String variant |
||||
private Optional<File> prebuiltBinary |
||||
|
||||
@OutputFile |
||||
File getPrebuiltBinaryFile() { |
||||
if (user == null) throw new GradleException("Github user isn't specified") |
||||
if (repository == null) repository = project.name |
||||
if (workflow == null) throw new GradleException("Workflow isn't specified") |
||||
|
||||
if (prebuiltBinary == null) { |
||||
if (githubAccessToken == null || githubAccessToken.isEmpty()) { |
||||
tokenWarning.log() |
||||
} |
||||
prebuiltBinary = getExternalBinary(variant) |
||||
} |
||||
|
||||
return prebuiltBinary.orElseGet { |
||||
String errorMessage = """Library for $variant could not be downloaded. |
||||
|Download it from $manualDownloadUrl |
||||
|""".stripMargin() |
||||
|
||||
if (missingLibraryIsFailure) { |
||||
throw new GradleException(format(errorMessage)) |
||||
} else { |
||||
new OneTimeLogger.Static({ |
||||
error(errorMessage) |
||||
}, "${variant}-missing").log() |
||||
} |
||||
return createDirectory(tempFilePath("dummy/")) |
||||
} |
||||
} |
||||
|
||||
void setMissingLibraryIsFailure(boolean missingLibraryIsFailure) { |
||||
this.missingLibraryIsFailure = missingLibraryIsFailure |
||||
} |
||||
|
||||
void setGithubAccessToken(String githubAccessToken) { |
||||
this.githubAccessToken = githubAccessToken |
||||
} |
||||
|
||||
void setVariant(String variant) { |
||||
this.variant = variant |
||||
} |
||||
|
||||
void setUser(String user) { |
||||
this.user = user |
||||
} |
||||
|
||||
void setRepository(String repository) { |
||||
this.repository = repository |
||||
} |
||||
|
||||
void setWorkflow(String workflow) { |
||||
this.workflow = workflow |
||||
} |
||||
|
||||
void setManualDownloadUrl(String manualDownloadUrl) { |
||||
this.manualDownloadUrl = manualDownloadUrl |
||||
} |
||||
|
||||
void setBranches(List<String> branches) { |
||||
this.branches = branches |
||||
} |
||||
|
||||
private Map getCacheInfo() { |
||||
if (cacheInfo == null) { |
||||
LOCK.readLock().lock() |
||||
try { |
||||
File cacheInfoFile = getCacheInfoFile() |
||||
JsonSlurper jsonParser = new JsonSlurper() |
||||
cacheInfo = jsonParser.parseText(cacheInfoFile.text) as Map |
||||
} finally { |
||||
LOCK.readLock().unlock() |
||||
} |
||||
} |
||||
return cacheInfo |
||||
} |
||||
|
||||
private File getCacheInfoFile() { |
||||
String path = preBuildPath(VERSION_INFO_FILE_NAME) |
||||
File cacheInfo = new File(path) |
||||
if (!cacheInfo.exists()) { |
||||
cacheInfo = createFile(path) |
||||
cacheInfo << "{}" |
||||
} |
||||
return cacheInfo |
||||
} |
||||
|
||||
private void writeToCache(String variantName, String timeStamp, File file) { |
||||
LOCK.writeLock().lock() |
||||
try { |
||||
Map cacheInfo = getCacheInfo() |
||||
Map entry = [timeStamp: timeStamp, path: file.absolutePath] |
||||
cacheInfo.put(variantName, entry) |
||||
getCacheInfoFile().write(JsonOutput.prettyPrint(JsonOutput.toJson(cacheInfo))) |
||||
} finally { |
||||
|
||||
} |
||||
} |
||||
|
||||
Optional<File> getExternalBinary(String variant) { |
||||
Tuple2<Optional<DownloadInfo>, Optional<File>> fetchResult = getBinaryDownloadUrl(variant) |
||||
Optional<DownloadInfo> downloadInfo = fetchResult.getFirst() |
||||
Optional<File> cachedFile = fetchResult.getSecond() |
||||
if (cachedFile.isPresent()) { |
||||
log("Reusing previously downloaded binary ${cachedFile.map { it.absolutePath }.orElse(null)}") |
||||
return cachedFile |
||||
} |
||||
Optional<File> downloadedFile = downloadInfo.map { |
||||
getBinaryFromUrl(variant, it.url).orElse(null) |
||||
} |
||||
|
||||
if (downloadedFile.isPresent()) { |
||||
writeToCache(variant, downloadInfo.get()?.timeStamp, downloadedFile.get()) |
||||
} else { |
||||
info("No file found for variant $variant") |
||||
} |
||||
|
||||
if (downloadedFile.isPresent()) return downloadedFile |
||||
return getCachedFile(variant) |
||||
} |
||||
|
||||
private Optional<File> getBinaryFromUrl(String variant, String url) { |
||||
File directory = createDirectory(preBuildPath(variant)) |
||||
info("Downloading binary for variant '$variant' from $url") |
||||
Optional<File> file = downloadZipFile(url, variant).map { unzip(it, directory).findFirst() }.orElse(Optional.empty()) |
||||
info("Finished download for variant '$variant'") |
||||
return file |
||||
} |
||||
|
||||
private String preBuildPath(String variant) { |
||||
return "${project.buildDir}${File.separator}$PRE_BUILD_PATH${File.separator}$variant" |
||||
} |
||||
|
||||
private Optional<ZipFile> downloadZipFile(String url, String variant) { |
||||
return fetch(url) { |
||||
File file = createFile(zipPath(variant)) |
||||
Path response = file.toPath() |
||||
Files.copy(it.getInputStream(), response, StandardCopyOption.REPLACE_EXISTING) |
||||
return new ZipFile(file) |
||||
} |
||||
} |
||||
|
||||
private String zipPath(String name) { |
||||
return tempFilePath("${name}.zip") |
||||
} |
||||
|
||||
private String tempFilePath(String name) { |
||||
return "$project.buildDir${File.separator}$TEMP_PATH${File.separator}${name}" |
||||
} |
||||
|
||||
private static Stream<File> unzip(ZipFile self, File directory) { |
||||
Collection<ZipEntry> files = self.entries().findAll { !(it as ZipEntry).directory } |
||||
return files.stream().map { |
||||
ZipEntry e = it as ZipEntry |
||||
e.name.with { fileName -> |
||||
File outputFile = createFile("${directory.path}$File.separator$fileName") |
||||
Files.copy(self.getInputStream(e), outputFile.toPath(), StandardCopyOption.REPLACE_EXISTING) |
||||
return outputFile |
||||
} |
||||
} |
||||
} |
||||
|
||||
private Tuple2<Optional<DownloadInfo>, Optional<File>> getBinaryDownloadUrl(String variantName) { |
||||
boolean isUptoDate = false |
||||
File cachedFile = null |
||||
String timeStamp = null |
||||
String artifactUrl = getLatestRun(getJson(getWorkflowsUrl())).with { |
||||
timeStamp = it.get("created_at") |
||||
Optional<String> cachedFilePath = getCachedFilePath(variantName, timeStamp) |
||||
isUptoDate = cachedFilePath.isPresent() |
||||
if (isUptoDate) { |
||||
cachedFile = new File(cachedFilePath.get()) |
||||
isUptoDate = cachedFile.exists() |
||||
} |
||||
return get("artifacts_url") as String |
||||
} |
||||
info("Latest artifact for variant '$variantName' is from $timeStamp") |
||||
if (isUptoDate) { |
||||
return new Tuple2<>(Optional.empty(), Optional.of(cachedFile)) |
||||
} |
||||
DownloadInfo downloadInfo = artifactUrl?.with { url -> |
||||
Map[] artifacts = getJson(url).get("artifacts") as Map[] |
||||
String artifactDownloadUrl = artifacts?.find { variantName == it.get("name") }?.get("url") as String |
||||
return artifactDownloadUrl?.with { |
||||
new DownloadInfo(getJson(it)?.get("archive_download_url") as String, timeStamp) |
||||
} |
||||
} |
||||
|
||||
return new Tuple2<>(Optional.ofNullable(downloadInfo), Optional.empty()) |
||||
} |
||||
|
||||
private String getWorkflowsUrl() { |
||||
return "https://api.github.com/repos/$user/$repository/actions/workflows/$workflow/runs" |
||||
} |
||||
|
||||
private Optional<String> getCachedFilePath(String variantName, String timeStamp) { |
||||
Map cacheInfo = getCacheInfo() |
||||
boolean isLatest = (cacheInfo[variantName] as Map)?.get("timeStamp") == timeStamp |
||||
if (isLatest) { |
||||
return Optional.ofNullable((cacheInfo[variantName] as Map)?.get("path") as String) |
||||
} else { |
||||
return Optional.empty() |
||||
} |
||||
} |
||||
|
||||
private Optional<File> getCachedFile(String variant) { |
||||
Map cacheInfo = getCacheInfo() |
||||
return Optional.ofNullable(cacheInfo[variant] as Map).map { |
||||
return new File(String.valueOf(it["path"])).with { |
||||
if (it.exists()) useCachedWarning.log() |
||||
it.exists() ? it : null |
||||
} |
||||
} |
||||
} |
||||
|
||||
private Map getLatestRun(Map json) { |
||||
Map[] runs = json.get("workflow_runs") as Map[] |
||||
return Optional.ofNullable(runs?.find { run -> |
||||
boolean completed = "completed" == run.get("status") |
||||
boolean success = "success" == run.get("conclusion") |
||||
boolean isCorrectBranch = branches.isEmpty() || branches.contains(run.get("head_branch")?.toString()) |
||||
return completed && success && isCorrectBranch |
||||
}).orElseGet { |
||||
log("No suitable workflow run found.") |
||||
return Collections.emptyMap() |
||||
} |
||||
} |
||||
|
||||
private Map getJson(String url) { |
||||
return fetch(url) { |
||||
JsonSlurper jsonParser = new JsonSlurper() |
||||
Map parsedJson = jsonParser.parseText(it.getInputStream().getText()) as Map |
||||
return parsedJson |
||||
}.orElse(Collections.emptyMap()) |
||||
} |
||||
|
||||
private <T> Optional<T> fetch(String url, Transformer<T, HttpURLConnection> transformer) { |
||||
info("Fetching $url") |
||||
if (isOffline()) return Optional.empty() |
||||
HttpURLConnection get = new URL(url).openConnection() as HttpURLConnection |
||||
get.setRequestMethod("GET") |
||||
githubAccessToken?.with { |
||||
get.setRequestProperty("Authorization", "token $it") |
||||
} |
||||
try { |
||||
def responseCode = get.getResponseCode() |
||||
if (responseCode == HttpURLConnection.HTTP_OK) { |
||||
return Optional.ofNullable(transformer.transform(get)) |
||||
} else { |
||||
log("Could not fetch $url. Response code '$responseCode'.") |
||||
} |
||||
} catch (IOException ignored) { |
||||
} |
||||
return Optional.empty() |
||||
} |
||||
|
||||
private static File createFile(String fileName) { |
||||
File file = new File(fileName) |
||||
if (file.exists()) file.delete() |
||||
file.getParentFile().mkdirs() |
||||
file.createNewFile() |
||||
return file |
||||
} |
||||
|
||||
private static File createDirectory(String fileName) { |
||||
File file = new File(fileName) |
||||
file.mkdirs() |
||||
return file |
||||
} |
||||
|
||||
private boolean isOffline() { |
||||
return project.getGradle().startParameter.isOffline() |
||||
} |
||||
|
||||
private void info(String message) { |
||||
project.logger.info(format(message)) |
||||
} |
||||
|
||||
private void log(String message) { |
||||
project.logger.warn(format(message)) |
||||
} |
||||
|
||||
private void error(String message) { |
||||
project.logger.error(format(message)) |
||||
} |
||||
|
||||
private String format(String message) { |
||||
String pad = " " * (project.name.size() + 2) |
||||
return "${project.name}: ${message.replace("\n", "\n$pad")}" |
||||
} |
||||
|
||||
private class DownloadInfo { |
||||
protected String url |
||||
protected String timeStamp |
||||
|
||||
private DownloadInfo(String url, String timeStamp) { |
||||
this.url = url |
||||
this.timeStamp = timeStamp |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,31 @@
|
||||
import dev.nokee.platform.nativebase.OperatingSystemFamily |
||||
import dev.nokee.platform.nativebase.TargetMachine |
||||
import org.gradle.api.GradleException |
||||
import org.gradle.api.Project |
||||
|
||||
class JniUtils { |
||||
static String asVariantName(TargetMachine targetMachine) { |
||||
String operatingSystemFamily = 'macos' |
||||
if (targetMachine.operatingSystemFamily.windows) { |
||||
operatingSystemFamily = 'windows' |
||||
} |
||||
|
||||
String architecture = 'x86-64' |
||||
if (targetMachine.architecture.'32Bit') { |
||||
architecture = 'x86' |
||||
} |
||||
|
||||
return "$operatingSystemFamily-$architecture" |
||||
} |
||||
|
||||
static String getLibraryFileNameFor(Project project, OperatingSystemFamily osFamily) { |
||||
if (osFamily.windows) { |
||||
return "${project.name}.dll" |
||||
} else if (osFamily.linux) { |
||||
return "lib${project.name}.so" |
||||
} else if (osFamily.macOS) { |
||||
return "lib${project.name}.dylib" |
||||
} |
||||
throw new GradleException("Unknown operating system family '${osFamily}'.") |
||||
} |
||||
} |
@ -0,0 +1,45 @@
|
||||
class OneTimeLogger { |
||||
private final Runnable logger |
||||
private boolean messageAlreadyLogged = false |
||||
|
||||
OneTimeLogger(Runnable logger) { |
||||
this.logger = logger |
||||
} |
||||
|
||||
protected boolean isLogged() { |
||||
return messageAlreadyLogged |
||||
} |
||||
|
||||
protected void setLogged(boolean logged) { |
||||
messageAlreadyLogged = logged |
||||
} |
||||
|
||||
protected void log() { |
||||
if (!isLogged()) { |
||||
logger.run() |
||||
setLogged(true) |
||||
} |
||||
} |
||||
|
||||
static class Static extends OneTimeLogger { |
||||
|
||||
private static final Map<Object, Boolean> isLogged = new HashMap<>() |
||||
|
||||
private final Object identifier |
||||
|
||||
Static(Runnable logger, Object identifier) { |
||||
super(logger) |
||||
this.identifier = identifier |
||||
} |
||||
|
||||
@Override |
||||
protected void setLogged(boolean logged) { |
||||
isLogged.put(identifier, logged) |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isLogged() { |
||||
return Boolean.TRUE == isLogged.get(identifier) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@
|
||||
import dev.nokee.platform.jni.JniJarBinary |
||||
import dev.nokee.platform.jni.JniLibrary |
||||
import dev.nokee.platform.jni.JniLibraryExtension |
||||
import dev.nokee.platform.nativebase.TargetMachine |
||||
import groovy.transform.CompileStatic |
||||
import org.gradle.api.Plugin |
||||
import org.gradle.api.Project |
||||
import org.gradle.api.Transformer |
||||
import org.gradle.api.file.CopySpec |
||||
import org.gradle.api.provider.Provider |
||||
import org.gradle.jvm.tasks.Jar |
||||
|
||||
@CompileStatic |
||||
class UberJniJarPlugin implements Plugin<Project> { |
||||
|
||||
@Override |
||||
void apply(Project project) { |
||||
project.tasks.named('jar', Jar) { task -> |
||||
configure(task) |
||||
} |
||||
} |
||||
|
||||
private static void configure(Jar task) { |
||||
def project = task.getProject() |
||||
def logger = task.getLogger() |
||||
def library = project.extensions.getByType(JniLibraryExtension) |
||||
library.binaries.withType(JniJarBinary).configureEach { |
||||
if (it.jarTask.isPresent()) it.jarTask.get()?.enabled = false |
||||
} |
||||
if (library.targetMachines.get().size() >= 1) { |
||||
logger.info("${project.name}: Merging binaries into the JVM Jar.") |
||||
for (TargetMachine targetMachine : library.targetMachines.get()) { |
||||
Provider<JniLibrary> variant = library.variants |
||||
.flatMap(targetMachineOf(targetMachine)) |
||||
.map(onlyOne() as Transformer<?, ? super List<?>>) as Provider<JniLibrary> |
||||
task.into(variant.map { it.resourcePath }) { CopySpec spec -> |
||||
spec.from(variant.map { it.nativeRuntimeFiles }) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Filter variants that match the specified target machine. |
||||
private static Transformer<Iterable<JniLibrary>, JniLibrary> targetMachineOf(TargetMachine targetMachine) { |
||||
return new Transformer<Iterable<JniLibrary>, JniLibrary>() { |
||||
@Override |
||||
Iterable<JniLibrary> transform(JniLibrary variant) { |
||||
if (variant.targetMachine == targetMachine) { |
||||
return [variant] |
||||
} |
||||
return [] |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Ensure only a single variant is present in the collection and return the variant. |
||||
private static Transformer<JniLibrary, List<? extends JniLibrary>> onlyOne() { |
||||
return new Transformer<JniLibrary, List<? extends JniLibrary>>() { |
||||
@Override |
||||
JniLibrary transform(List<? extends JniLibrary> variants) { |
||||
assert variants.size() == 1 |
||||
return variants.first() |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,164 @@
|
||||
import dev.nokee.platform.jni.JniLibraryExtension |
||||
import groovy.transform.CompileStatic |
||||
import org.gradle.api.Action |
||||
import org.gradle.api.Plugin |
||||
import org.gradle.api.Project |
||||
import org.gradle.api.plugins.ExtensionAware |
||||
|
||||
@CompileStatic |
||||
class UsePrebuiltBinariesWhenUnbuildablePlugin implements Plugin<Project> { |
||||
|
||||
private PrebuildBinariesExtension prebuildExtension |
||||
private GithubArtifactExtension githubArtifactExtension |
||||
|
||||
void prebuildBinaries(Action<? extends PrebuildBinariesExtension> action) { |
||||
action.execute(prebuildExtension) |
||||
} |
||||
|
||||
PrebuildBinariesExtension getPrebuildBinaries() { |
||||
return prebuildExtension |
||||
} |
||||
|
||||
@Override |
||||
void apply(Project project) { |
||||
JniLibraryExtension library = project.extensions.getByType(JniLibraryExtension) |
||||
prebuildExtension = project.extensions.create("prebuildBinaries", PrebuildBinariesExtension, this) |
||||
githubArtifactExtension = (prebuildExtension as ExtensionAware).with { |
||||
it.extensions.create("github", GithubArtifactExtension) |
||||
} |
||||
library.variants.configureEach { var -> |
||||
if (prebuildExtension.alwaysUsePrebuildArtifact || !var.sharedLibrary.buildable) { |
||||
// Try to include the library file... if available |
||||
def defaultLibraryName = JniUtils.getLibraryFileNameFor(project, var.targetMachine.operatingSystemFamily) |
||||
def variantName = JniUtils.asVariantName(var.targetMachine) |
||||
def libraryFile = project.file( |
||||
"${prebuildExtension.prebuildLibrariesFolder}/$variantName/$defaultLibraryName" |
||||
) |
||||
|
||||
if (!libraryFile.exists()) { |
||||
// No local binary provided. Try to download it from github actions. |
||||
def prebuiltBinariesTask = project.tasks.register("downloadPrebuiltBinary$variantName", DownloadPrebuiltBinaryFromGitHubAction.class) |
||||
prebuiltBinariesTask.configure { |
||||
it.githubAccessToken = githubArtifactExtension.accessToken |
||||
it.variant = variantName |
||||
it.user = githubArtifactExtension.user |
||||
it.repository = githubArtifactExtension.repository |
||||
it.workflow = githubArtifactExtension.workflow |
||||
it.manualDownloadUrl = githubArtifactExtension.manualDownloadUrl |
||||
it.branches = githubArtifactExtension.branches |
||||
it.missingLibraryIsFailure = prebuildExtension.missingLibraryIsFailure |
||||
} |
||||
var.nativeRuntimeFiles.setFrom(prebuiltBinariesTask.map { it.prebuiltBinaryFile }) |
||||
var.nativeRuntimeFiles.from(new CallableLogger({ |
||||
project.logger.warn("${project.name}: Using pre-build library from github for targetMachine $variantName.") |
||||
})) |
||||
} else { |
||||
//Use provided library. |
||||
var.nativeRuntimeFiles.setFrom(libraryFile) |
||||
var.nativeRuntimeFiles.from(new CallableLogger({ |
||||
def relativePath = project.rootProject.relativePath(libraryFile) |
||||
project.logger.warn("${project.name}: Using pre-build library $relativePath for targetMachine $variantName.") |
||||
})) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static class PrebuildBinariesExtension { |
||||
|
||||
private String prebuildLibrariesFolder = "pre-build-libraries" |
||||
private boolean alwaysUsePrebuildArtifact = false |
||||
private boolean missingLibraryIsFailure = true |
||||
private UsePrebuiltBinariesWhenUnbuildablePlugin plugin |
||||
|
||||
PrebuildBinariesExtension(UsePrebuiltBinariesWhenUnbuildablePlugin plugin) { |
||||
this.plugin = plugin |
||||
} |
||||
|
||||
void github(Action<? extends GithubArtifactExtension> action) { |
||||
action.execute(plugin.githubArtifactExtension) |
||||
} |
||||
|
||||
void setAlwaysUsePrebuildArtifact(boolean alwaysUsePrebuildArtifact) { |
||||
this.alwaysUsePrebuildArtifact = alwaysUsePrebuildArtifact |
||||
} |
||||
|
||||
boolean getAlwaysUsePrebuildArtifact() { |
||||
return alwaysUsePrebuildArtifact |
||||
} |
||||
|
||||
String getPrebuildLibrariesFolder() { |
||||
return prebuildLibrariesFolder |
||||
} |
||||
|
||||
boolean getMissingLibraryIsFailure() { |
||||
return missingLibraryIsFailure |
||||
} |
||||
|
||||
void setPrebuildLibrariesFolder(String prebuildLibrariesFolder) { |
||||
this.prebuildLibrariesFolder = prebuildLibrariesFolder |
||||
} |
||||
|
||||
void setMissingLibraryIsFailure(boolean missingLibraryIsFailure) { |
||||
this.missingLibraryIsFailure = missingLibraryIsFailure |
||||
} |
||||
} |
||||
|
||||
static class GithubArtifactExtension { |
||||
private String user |
||||
private String repository |
||||
private String workflow |
||||
private String manualDownloadUrl |
||||
private String accessToken |
||||
private List<String> branches = ["master"] |
||||
|
||||
String getUser() { |
||||
return user |
||||
} |
||||
|
||||
String getRepository() { |
||||
return repository |
||||
} |
||||
|
||||
String getWorkflow() { |
||||
return workflow |
||||
} |
||||
|
||||
String getManualDownloadUrl() { |
||||
return manualDownloadUrl |
||||
} |
||||
|
||||
String getAccessToken() { |
||||
return accessToken |
||||
} |
||||
|
||||
List<String> getBranches() { |
||||
return branches |
||||
} |
||||
|
||||
void setUser(String user) { |
||||
this.user = user |
||||
} |
||||
|
||||
void setRepository(String repository) { |
||||
this.repository = repository |
||||
} |
||||
|
||||
void setWorkflow(String workflow) { |
||||
this.workflow = workflow |
||||
} |
||||
|
||||
void setManualDownloadUrl(String manualDownloadUrl) { |
||||
this.manualDownloadUrl = manualDownloadUrl |
||||
} |
||||
|
||||
void setAccessToken(String accessToken) { |
||||
this.accessToken = accessToken |
||||
} |
||||
|
||||
void setBranches(List<String> branches) { |
||||
this.branches = branches |
||||
} |
||||
} |
||||
|
||||
} |
@ -1,18 +0,0 @@
|
||||
import org.gradle.api.DefaultTask |
||||
import org.gradle.api.tasks.Internal |
||||
import org.gradle.api.tasks.TaskAction |
||||
import org.gradle.nativeplatform.toolchain.internal.xcode.MacOSSdkPathLocator |
||||
import java.io.File |
||||
import javax.inject.Inject |
||||
|
||||
open class MacOSSdkPathTask @Inject constructor( |
||||
private val locator: MacOSSdkPathLocator |
||||
) : DefaultTask() { |
||||
@Internal |
||||
lateinit var sdkPath: File |
||||
|
||||
@TaskAction |
||||
fun run() { |
||||
sdkPath = locator.find() |
||||
} |
||||
} |
@ -1,126 +0,0 @@
|
||||
import org.gradle.internal.jvm.Jvm |
||||
import org.gradle.kotlin.dsl.invoke |
||||
|
||||
plugins { |
||||
`cpp-library` |
||||
`java-library` |
||||
} |
||||
|
||||
// This configuration might be used for adding cpp-only dependencies |
||||
val jniImplementation by configurations.creating |
||||
|
||||
val defaultLibraryName: String by project |
||||
|
||||
configurations.matching { |
||||
it.name.startsWith("cppCompile") || |
||||
it.name.startsWith("nativeLink") || |
||||
it.name.startsWith("nativeRuntime") |
||||
}.all { |
||||
extendsFrom(jniImplementation) |
||||
} |
||||
|
||||
tasks.compileJava { |
||||
options.headerOutputDirectory.convention( |
||||
project.layout.buildDirectory.dir("generated/jni-headers") |
||||
) |
||||
// The nested output is not marked automatically as an output of the task regarding task dependencies. |
||||
// So we mark it manually here. |
||||
// See https://github.com/gradle/gradle/issues/6619. |
||||
outputs.dir(options.headerOutputDirectory) |
||||
// Cannot do incremental header generation, since the pattern for cleaning them up is currently wrong. |
||||
// See https://github.com/gradle/gradle/issues/12084. |
||||
options.isIncremental = false |
||||
} |
||||
|
||||
tasks.withType<CppCompile>().configureEach { |
||||
includes(tasks.compileJava.flatMap { it.options.headerOutputDirectory }) |
||||
} |
||||
|
||||
library { |
||||
binaries.configureEach { |
||||
val targetOs = targetMachine.operatingSystemFamily |
||||
compileTask.get().apply { |
||||
val javaHome = Jvm.current().javaHome.canonicalPath |
||||
includes("$javaHome/include") |
||||
includes(when { |
||||
targetOs.isMacOs -> listOf("$javaHome/include/darwin") |
||||
targetOs.isLinux -> listOf("$javaHome/include/linux") |
||||
targetOs.isWindows -> listOf("$javaHome/include/win32") |
||||
else -> emptyList() |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Gradle does not support [Provider] for [JavaForkOptions.systemProperty], so |
||||
* we pass an object that overrides [Any.toString]. |
||||
*/ |
||||
fun Provider<String>.overrideToString() = object { |
||||
override fun toString() = orNull ?: "" |
||||
} |
||||
|
||||
val TargetMachine.variantName: String get() = "$operatingSystemFamily-$architecture" |
||||
|
||||
// Gradle populates library.binaries in afterEvaluate, so we can't access it earlier |
||||
afterEvaluate { |
||||
// C++ library is built for Windows/macOS only, so we skip it otherwise |
||||
library.developmentBinary.orNull?.let { it as CppSharedLibrary }?.let { developmentBinary -> |
||||
tasks.test { |
||||
dependsOn(developmentBinary.linkTask) |
||||
val libraryDir = developmentBinary.runtimeFile |
||||
.map { it.asFile.parentFile.absolutePath } |
||||
systemProperty("java.library.path", libraryDir.overrideToString()) |
||||
} |
||||
} |
||||
tasks.jar { |
||||
//Disable all task. Tasks are reenabled if needed. |
||||
library.binaries.get().forEach { |
||||
it.compileTask.get().enabled = false |
||||
} |
||||
library.binaries.get() |
||||
.filter { it.isOptimized } |
||||
.filterIsInstance<CppSharedLibrary>().let { |
||||
val taskMap = it.map { binary -> binary.targetPlatform.targetMachine to binary }.toMap() |
||||
library.targetMachines.get().forEach { targetMachine -> |
||||
val libraryPath = "com/github/weisj/darklaf/platform/${project.name}" |
||||
val variantName = targetMachine.variantName |
||||
val libraryFile = file("libraries/$variantName/$defaultLibraryName") |
||||
val relativePath = rootProject.relativePath(libraryFile) |
||||
when { |
||||
libraryFile.exists() -> { |
||||
//Use provided library. |
||||
logger.warn( |
||||
"${project.name}: Using pre-build library $relativePath for targetMachine $variantName." |
||||
) |
||||
into("$libraryPath/$variantName") { |
||||
from(libraryFile) |
||||
} |
||||
} |
||||
targetMachine in taskMap -> { |
||||
taskMap[targetMachine]?.let { binary -> |
||||
binary.compileTask.get().enabled = true |
||||
// Publish optimized binary to reduce size. |
||||
binary.linkTask.get().debuggable.set(false) |
||||
//Build and copy library |
||||
dependsOn(binary.linkTask) |
||||
into("$libraryPath/$variantName") { |
||||
from(binary.runtimeFile) |
||||
} |
||||
} |
||||
} |
||||
else -> { |
||||
val downloadUrl = |
||||
"https://github.com/weisJ/darklaf/actions?query=workflow%3A%22Build+Native+Libraries%22+is%3Asuccess+branch%3Amaster" |
||||
logger.warn( |
||||
""" |
||||
${project.name}: Library $relativePath for targetMachine $variantName does not exist. |
||||
${" ".repeat(project.name.length + 1)} Download it from $downloadUrl |
||||
""".trimIndent() |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,52 +1,51 @@
|
||||
plugins { |
||||
`jni-library` |
||||
} |
||||
|
||||
fun DependencyHandlerScope.javaImplementation(dep: Any) { |
||||
compileOnly(dep) |
||||
runtimeOnly(dep) |
||||
} |
||||
import JniUtils.asVariantName |
||||
|
||||
dependencies { |
||||
javaImplementation(project(":darklaf-theme")) |
||||
javaImplementation(project(":darklaf-native-utils")) |
||||
javaImplementation(project(":darklaf-utils")) |
||||
javaImplementation(project(":darklaf-platform-base")) |
||||
javaImplementation(project(":darklaf-property-loader")) |
||||
plugins { |
||||
java |
||||
id("dev.nokee.jni-library") |
||||
id("dev.nokee.objective-cpp-language") |
||||
`uber-jni-jar` |
||||
`use-prebuilt-binaries` |
||||
} |
||||
|
||||
val macPath by tasks.registering(MacOSSdkPathTask::class) |
||||
|
||||
val sdkRoot: Provider<String> get() = macPath.map { it.sdkPath.absolutePath } |
||||
library { |
||||
val minOs = "10.10" |
||||
val frameworkVersion = "10.15" |
||||
|
||||
fun ListProperty<String>.addJavaFrameworks() { |
||||
addAll("-framework", "JavaNativeFoundation") |
||||
add("-F") |
||||
add(sdkRoot.map { "$it/System/Library/Frameworks/JavaVM.framework/Frameworks" }) |
||||
add("-F") |
||||
add("/System/Library/Frameworks/JavaVM.framework/Frameworks") |
||||
} |
||||
dependencies { |
||||
jvmImplementation(project(":darklaf-theme")) |
||||
jvmImplementation(project(":darklaf-native-utils")) |
||||
jvmImplementation(project(":darklaf-utils")) |
||||
jvmImplementation(project(":darklaf-platform-base")) |
||||
jvmImplementation(project(":darklaf-property-loader")) |
||||
nativeImplementation("dev.nokee.framework:JavaVM:[$frameworkVersion,)") |
||||
nativeImplementation("dev.nokee.framework:JavaVM:[$frameworkVersion,)") { |
||||
capabilities { |
||||
requireCapability("JavaVM:JavaNativeFoundation:[$frameworkVersion,)") |
||||
} |
||||
} |
||||
nativeImplementation("dev.nokee.framework:AppKit:[$frameworkVersion,)") |
||||
nativeImplementation("dev.nokee.framework:Cocoa:[$frameworkVersion,)") |
||||
} |
||||
|
||||
library { |
||||
targetMachines.addAll(machines.macOS.x86_64) |
||||
binaries.configureEach { |
||||
compileTask.get().apply { |
||||
dependsOn(macPath) |
||||
compilerArgs.addAll("-x", "objective-c++") |
||||
compilerArgs.addAll("-mmacosx-version-min=10.10") |
||||
compilerArgs.addAll("-Wunguarded-availability") |
||||
compilerArgs.addJavaFrameworks() |
||||
source.from( |
||||
file("src/main/objectiveCpp/Decorations.mm"), |
||||
file("src/main/objectiveCpp/ThemeInfo.mm") |
||||
) |
||||
} |
||||
} |
||||
binaries.whenElementFinalized(CppSharedLibrary::class) { |
||||
linkTask.get().apply { |
||||
dependsOn(macPath) |
||||
linkerArgs.addAll("-lobjc", "-framework", "AppKit") |
||||
linkerArgs.addJavaFrameworks() |
||||
variants.configureEach { |
||||
resourcePath.set("com/github/weisj/darklaf/platform/${project.name}/${asVariantName(targetMachine)}") |
||||
sharedLibrary { |
||||
compileTasks.configureEach { |
||||
compilerArgs.addAll("-mmacosx-version-min=$minOs") |
||||
// Build type not modeled yet, assuming release |
||||
compilerArgs.addAll(toolChain.map { |
||||
when (it) { |
||||
is Gcc, is Clang -> listOf("-O2") |
||||
is VisualCpp -> listOf("/O2") |
||||
else -> emptyList() |
||||
} |
||||
}) |
||||
} |
||||
linkTask.configure { |
||||
linkerArgs.addAll("-lobjc", "-mmacosx-version-min=$minOs") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
@ -1 +0,0 @@
|
||||
defaultLibraryName = libdarklaf-macos.dylib |
@ -1,41 +1,54 @@
|
||||
|
||||
import JniUtils.asVariantName |
||||
|
||||
plugins { |
||||
`jni-library` |
||||
} |
||||
|
||||
fun DependencyHandlerScope.javaImplementation(dep: Any) { |
||||
compileOnly(dep) |
||||
runtimeOnly(dep) |
||||
} |
||||
|
||||
dependencies { |
||||
javaImplementation(project(":darklaf-native-utils")) |
||||
javaImplementation(project(":darklaf-utils")) |
||||
javaImplementation(project(":darklaf-platform-base")) |
||||
javaImplementation(project(":darklaf-theme")) |
||||
javaImplementation(project(":darklaf-property-loader")) |
||||
javaImplementation("net.java.dev.jna:jna") |
||||
java |
||||
id("dev.nokee.jni-library") |
||||
id("dev.nokee.cpp-language") |
||||
`uber-jni-jar` |
||||
`use-prebuilt-binaries` |
||||
} |
||||
|
||||
library { |
||||
dependencies { |
||||
jvmImplementation(project(":darklaf-native-utils")) |
||||
jvmImplementation(project(":darklaf-utils")) |
||||
jvmImplementation(project(":darklaf-platform-base")) |
||||
jvmImplementation(project(":darklaf-theme")) |
||||
jvmImplementation(project(":darklaf-property-loader")) |
||||
jvmImplementation("net.java.dev.jna:jna") |
||||
} |
||||
|
||||
targetMachines.addAll(machines.windows.x86, machines.windows.x86_64) |
||||
binaries.configureEach { |
||||
compileTask.get().compilerArgs.addAll( |
||||
when (toolChain) { |
||||
variants.configureEach { |
||||
resourcePath.set("com/github/weisj/darklaf/platform/${project.name}/${asVariantName(targetMachine)}") |
||||
sharedLibrary { |
||||
compileTasks.configureEach { |
||||
compilerArgs.addAll(toolChain.map { |
||||
when (it) { |
||||
is Gcc, is Clang -> listOf("--std=c++11") |
||||
is VisualCpp -> listOf("/EHsc") |
||||
else -> emptyList() |
||||
} |
||||
) |
||||
}) |
||||
|
||||
// Build type not modeled yet, assuming release |
||||
compilerArgs.addAll(toolChain.map { |
||||
when (it) { |
||||
is Gcc, is Clang -> listOf("-O2") |
||||
is VisualCpp -> listOf("/O2") |
||||
else -> emptyList() |
||||
} |
||||
}) |
||||
} |
||||
binaries.whenElementFinalized(CppSharedLibrary::class) { |
||||
linkTask.get().linkerArgs.addAll( |
||||
when (toolChain) { |
||||
linkTask.configure { |
||||
linkerArgs.addAll(toolChain.map { |
||||
when (it) { |
||||
is Gcc, is Clang -> listOf("-ldwmapi", "-lGdi32", "-luser32", "-ladvapi32", "-Shell32") |
||||
is VisualCpp -> listOf("dwmapi.lib", "user32.lib", "Gdi32.lib", "Advapi32.lib", "Shell32.lib") |
||||
else -> emptyList() |
||||
} |
||||
) |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue