Skip to content

Commit

Permalink
Add support for shared libraries (#1158)
Browse files Browse the repository at this point in the history
* Add support for shared libraries

* Extract iOS dummy symbols

* Add missing file
  • Loading branch information
José Pereda authored Jul 21, 2022
1 parent 9aa5e2e commit a1a6a37
Show file tree
Hide file tree
Showing 14 changed files with 317 additions and 101 deletions.
19 changes: 17 additions & 2 deletions src/main/java/com/gluonhq/substrate/SubstrateDispatcher.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -31,9 +31,9 @@
import com.gluonhq.substrate.model.ProcessPaths;
import com.gluonhq.substrate.model.Triplet;
import com.gluonhq.substrate.target.AndroidTargetConfiguration;
import com.gluonhq.substrate.target.MacOSTargetConfiguration;
import com.gluonhq.substrate.target.IosTargetConfiguration;
import com.gluonhq.substrate.target.LinuxTargetConfiguration;
import com.gluonhq.substrate.target.MacOSTargetConfiguration;
import com.gluonhq.substrate.target.TargetConfiguration;
import com.gluonhq.substrate.target.WebTargetConfiguration;
import com.gluonhq.substrate.target.WindowsTargetConfiguration;
Expand Down Expand Up @@ -495,4 +495,19 @@ public void nativeRun() throws IOException, InterruptedException {
targetConfiguration.runUntilEnd();
printMessage("run");
}

/**
* This method builds a native image that can be used as shared library by third
* party projects, considering it contains one or more entry points.
*
* Static entry points, callable from C, can be created with the {@code @CEntryPoint}
* annotation.
*
* @throws Exception
*/
public boolean nativeSharedLibrary() throws Exception {
Logger.logInfo(logTitle("SHARED LIBRARY TASK"));
config.setSharedLibrary(true);
return targetConfiguration.createSharedLib();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public class InternalProjectConfiguration {
private boolean usePrismSW = false;
private boolean enableCheckHash = true;
private boolean usesJDK11 = false;
private boolean sharedLibrary = false;

private String backend;
private List<String> initBuildTimeList;
Expand Down Expand Up @@ -326,6 +327,14 @@ public void setEnableCheckHash(boolean enableCheckHash) {
this.enableCheckHash = enableCheckHash;
}

public boolean isSharedLibrary() {
return sharedLibrary;
}

public void setSharedLibrary(boolean sharedLibrary) {
this.sharedLibrary = sharedLibrary;
}

public Triplet getTargetTriplet() {
return Objects.requireNonNull( publicConfig.getTargetTriplet(), "Target triplet is required");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ public abstract class AbstractTargetConfiguration implements TargetConfiguration
private static final List<String> baseNativeImageArguments = Arrays.asList(
"-Djdk.internal.lambda.eagerlyInitialize=false",
"--no-server",
"-H:+ExitAfterRelocatableImageWrite",
"-H:+SharedLibrary",
"-H:+AddAllCharsets",
"-H:+ReportExceptionStackTraces",
Expand Down Expand Up @@ -140,6 +139,11 @@ public boolean compile() throws IOException, InterruptedException {

baseNativeImageArguments.forEach(compileRunner::addArg);

if (!projectConfiguration.isSharedLibrary() ||
!projectConfiguration.getTargetTriplet().equals(Triplet.fromCurrentOS())) {
compileRunner.addArg("-H:+ExitAfterRelocatableImageWrite");
}

compileRunner.addArgs(getEnabledFeatures());

compileRunner.addArg(createTempDirectoryArg());
Expand Down Expand Up @@ -302,6 +306,17 @@ public boolean runUntilEnd() throws IOException, InterruptedException {
return result == 0;
}

/**
* Creates a native image that can be used as shared library
* @return true if the process succeeded or false if the process failed
* @throws IOException
* @throws InterruptedException
*/
@Override
public boolean createSharedLib() throws IOException, InterruptedException {
return true;
}

// --- private methods

protected boolean compileAdditionalSources()
Expand All @@ -311,6 +326,10 @@ protected boolean compileAdditionalSources()
Path workDir = paths.getGvmPath().resolve(appName);
Files.createDirectories(workDir);

if (getAdditionalSourceFiles().isEmpty()) {
return true;
}

ProcessRunner processRunner = new ProcessRunner(getCompiler());
processRunner.addArg("-c");
if (projectConfiguration.isVerbose()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -78,6 +78,7 @@ public class AndroidTargetConfiguration extends PosixTargetConfiguration {
private final Path objdump;
private final String hostPlatformFolder;

private final List<String> androidAdditionalDummySourceFiles = List.of("dummy.c");
private final List<String> androidAdditionalSourceFiles = Arrays.asList("launcher.c", "javafx_adapter.c",
"touch_events.c", "glibc_shim.c", "attach_adapter.c", "logger.c");
private final List<String> androidAdditionalWebSourceFiles = Collections.singletonList("bridge_webview.c");
Expand Down Expand Up @@ -342,9 +343,12 @@ public String getAdditionalSourceFileLocation() {

@Override
List<String> getAdditionalSourceFiles() {
List<String> answer = new ArrayList<>(androidAdditionalSourceFiles);
if (projectConfiguration.hasWeb()) {
answer.addAll(androidAdditionalWebSourceFiles);
List<String> answer = new ArrayList<>(androidAdditionalDummySourceFiles);
if (!projectConfiguration.isSharedLibrary()) {
answer.addAll(androidAdditionalSourceFiles);
if (projectConfiguration.hasWeb()) {
answer.addAll(androidAdditionalWebSourceFiles);
}
}
return answer;
}
Expand Down Expand Up @@ -667,4 +671,9 @@ private void copyAarLibraries() throws IOException, InterruptedException {
FileOps.extractFilesFromJar(".aar", jar.toPath(), libPath, null);
}
}

@Override
Path getSharedLibPath() {
return paths.getAppPath().resolve(getLinkOutputName());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Gluon
* Copyright (c) 2020, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -29,11 +29,25 @@

import com.gluonhq.substrate.model.InternalProjectConfiguration;
import com.gluonhq.substrate.model.ProcessPaths;
import com.gluonhq.substrate.util.ProcessRunner;

import java.io.IOException;
import java.nio.file.Path;

abstract class DarwinTargetConfiguration extends PosixTargetConfiguration {

DarwinTargetConfiguration(ProcessPaths paths, InternalProjectConfiguration configuration) {
super(paths, configuration);
}

@Override
public boolean createSharedLib() throws IOException, InterruptedException {
if (!super.createSharedLib()) {
return false;
}
Path lib = getSharedLibPath();
String libName = lib.getName(lib.getNameCount() - 1).toString();
ProcessRunner process = new ProcessRunner("install_name_tool", "-id", "@rpath/" + libName, libName);
return process.runProcess("install name", lib.getParent().toFile()) == 0;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -52,6 +52,7 @@

public class IosTargetConfiguration extends DarwinTargetConfiguration {

private static final List<String> iosAdditionalDummySourceFiles = List.of("dummy.c");
private static final List<String> iosAdditionalSourceFiles = Arrays.asList(
"AppDelegate.m", "JvmFuncsFallbacks.c");

Expand Down Expand Up @@ -100,6 +101,12 @@ List<String> getTargetSpecificLinkFlags(boolean useJavaFX, boolean usePrismSW) {
"-arch", getTargetArch(),
"-mios-version-min=11.0",
"-isysroot", getSysroot()));
if (projectConfiguration.isSharedLibrary()) {
linkFlags.addAll(Arrays.asList(
"-shared",
"-undefined",
"dynamic_lookup"));
}
if (useJavaFX) {
String javafxSDK = projectConfiguration.getJavafxStaticLibsPath().toString();
List<String> libs = new ArrayList<>(javafxLibs);
Expand Down Expand Up @@ -161,7 +168,19 @@ public String getAdditionalSourceFileLocation() {

@Override
List<String> getAdditionalSourceFiles() {
return iosAdditionalSourceFiles;
List<String> answer = new ArrayList<>(iosAdditionalDummySourceFiles);
if (!projectConfiguration.isSharedLibrary()) {
answer.addAll(iosAdditionalSourceFiles);
}
return answer;
}

@Override
List<String> getTargetSpecificLinkOutputFlags() {
if (projectConfiguration.isSharedLibrary()) {
return Arrays.asList("-o", getSharedLibPath().toString());
}
return super.getTargetSpecificLinkOutputFlags();
}

@Override
Expand Down Expand Up @@ -394,4 +413,9 @@ private Path getCapCacheDir() throws IOException {
return capPath;
}

@Override
Path getSharedLibPath() {
return paths.getAppPath().resolve(getLinkOutputName() + ".dylib");
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -240,6 +240,14 @@ List<String> getOtherStaticLibs() {
.collect(Collectors.toList());
}

@Override
List<String> getAdditionalSourceFiles() {
if (projectConfiguration.isSharedLibrary()) {
return List.of();
}
return super.getAdditionalSourceFiles();
}

@Override
protected List<Path> getLinkerLibraryPaths() throws IOException {
List<Path> linkerLibraryPaths = new ArrayList<>();
Expand All @@ -255,6 +263,10 @@ List<String> getTargetSpecificLinkFlags(boolean useJavaFX, boolean usePrismSW) t
List<String> answer = new LinkedList<>();
answer.add("-Wl,--wrap=pow");
answer.add("-rdynamic");
if (projectConfiguration.isSharedLibrary()) {
answer.add("-shared");
answer.add("-undefined");
}
if (crossCompile) {
answer.add("-fuse-ld=gold");
answer.add("--sysroot");
Expand Down Expand Up @@ -337,6 +349,14 @@ protected List<String> getTargetSpecificCCompileFlags() {
return flags;
}

@Override
List<String> getTargetSpecificLinkOutputFlags() {
if (projectConfiguration.isSharedLibrary()) {
return Arrays.asList("-o", getSharedLibPath().toString());
}
return super.getTargetSpecificLinkOutputFlags();
}

/*
* Copies the .cap files from the jar resource and store them in
* a directory. Return that directory
Expand Down Expand Up @@ -430,4 +450,9 @@ private boolean checkFileArchitecture(Path path) {
Logger.logDebug("Ignore file " + path + " since objdump failed on it");
return false;
}

@Override
Path getSharedLibPath() {
return paths.getAppPath().resolve(getLinkOutputName() + ".so");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -83,9 +83,20 @@ String getAdditionalSourceFileLocation() {

@Override
List<String> getAdditionalSourceFiles() {
if (projectConfiguration.isSharedLibrary()) {
return List.of();
}
return Arrays.asList("AppDelegate.m", "launcher.c");
}

@Override
List<String> getTargetSpecificLinkOutputFlags() {
if (projectConfiguration.isSharedLibrary()) {
return Arrays.asList("-o", getSharedLibPath().toString());
}
return super.getTargetSpecificLinkOutputFlags();
}

@Override
List<String> getTargetSpecificCCompileFlags() {
return Arrays.asList("-mmacosx-version-min=" + MIN_MACOS_VERSION);
Expand All @@ -96,7 +107,12 @@ List<String> getTargetSpecificLinkFlags(boolean useJavaFX, boolean usePrismSW) {
List<String> linkFlags = new ArrayList<>(asListOfLibraryLinkFlags(javaDarwinLibs));

linkFlags.add("-mmacosx-version-min=" + MIN_MACOS_VERSION);

if (projectConfiguration.isSharedLibrary()) {
linkFlags.addAll(Arrays.asList(
"-shared",
"-undefined",
"dynamic_lookup"));
}
if (useJavaFX) {
linkFlags.addAll(asListOfLibraryLinkFlags(javaFxDarwinLibs));
if (projectConfiguration.hasWeb()) {
Expand Down Expand Up @@ -217,4 +233,9 @@ private List<String> asListOfFrameworkLinkFlags(List<String> frameworks) {
.map(framework -> "-Wl,-framework," + framework)
.collect(Collectors.toList());
}

@Override
Path getSharedLibPath() {
return paths.getAppPath().resolve(getLinkOutputName() + ".dylib");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Gluon
* Copyright (c) 2019, 2022, Gluon
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -29,6 +29,7 @@

import com.gluonhq.substrate.model.InternalProjectConfiguration;
import com.gluonhq.substrate.model.ProcessPaths;
import com.gluonhq.substrate.util.Logger;

import java.io.IOException;
import java.nio.file.Files;
Expand All @@ -46,4 +47,19 @@ void checkPlatformSpecificClibs(Path clibPath) throws IOException {
if (!Files.exists(libjvmPath)) throw new IOException("Missing library libjvm.a not in linkpath "+clibPath);
}

@Override
public boolean createSharedLib() throws IOException, InterruptedException {
if (!compile()) {
Logger.logSevere("Error building a shared image: error compiling the native image");
return false;
}
if (!link()) {
Logger.logSevere("Error building a shared image: error linking the native image");
return false;
}
return Files.exists(getSharedLibPath());
}

abstract Path getSharedLibPath();

}
Loading

0 comments on commit a1a6a37

Please sign in to comment.