Skip to content

Commit

Permalink
Merge pull request #502 from defold/dotnet-win32-support
Browse files Browse the repository at this point in the history
Added DotNet support for Windows target platform
  • Loading branch information
JCash authored Oct 15, 2024
2 parents 272439c + 6d634b7 commit 0e51936
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 39 deletions.
9 changes: 8 additions & 1 deletion server/docker/Dockerfile.winsdk.2022-env
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,11 @@ RUN \
echo "WIN32 WindowsKits ${WINDOWS_SDK_10_20348_VERSION} - OpenGL Cleanup" && \
cd ${WINDOWS_SDK_10_DIR}/Include/${WINDOWS_SDK_10_20348_VERSION}/um && \
mkdir ./GL && \
cp -v ./gl/*.* ./GL/
cp -v ./gl/*.* ./GL/

# Since dotnet cannot really cross compile, we need to create a "lib" shim for "wine lib.exe"
# As long as it's in the path, it will be picked up
RUN \
echo '#!/usr/bin/env bash' > /usr/bin/lib && \
echo 'wine ${WINDOWS_VCINSTALLDIR_2022}/bin/Hostx64/x64/lib.exe $*' >> /usr/bin/lib && \
chmod +x /usr/bin/lib
21 changes: 16 additions & 5 deletions server/src/main/java/com/defold/extender/Extender.java
Original file line number Diff line number Diff line change
Expand Up @@ -1075,11 +1075,9 @@ private List<File> buildExtensionInternal_CSharp(File manifest, Map<String, Obje
throw new ExtenderException(String.format("%s:1: error: We currently don't support merging or building two library files at the same time!", ExtenderUtil.getRelativePath(this.uploadDirectory, manifest)));
}

// // Create static library
// Create static library
File library = getStaticLibraryFile(manifestContext, libraryOut);
String name = library.getName();
if (name.startsWith("lib"))
name = name.substring(3);
name = name.substring(0, name.lastIndexOf('.'));

File extDir = manifest.getParentFile();
Expand All @@ -1090,11 +1088,24 @@ private List<File> buildExtensionInternal_CSharp(File manifest, Map<String, Obje
File sdkProject = new File(sdkCsdmSDKDir, "dmsdk.csproj");

Map<String, Object> context = createContext(manifestContext);

// Make sure the engine libraries aren't starting with "lib" (i.e. "libextension" -> "extension")
List<String> libs = (List<String>)context.get("engineLibs");
if (ExtenderUtil.isWindowsTarget(this.platform))
{
libs = new ArrayList<>();
for (String lib : (List<String>)context.get("engineLibs")) {
if (lib.startsWith("lib"))
lib = lib.substring(3);
libs.add(lib);
}
}

CSharpBuilder csBuilder = new CSharpBuilder(processExecutor, templateExecutor, context);
csBuilder.setSdkProject(sdkProject);
csBuilder.setSourceDirectory(extDir);
csBuilder.setOutputDirectory(new File(buildDirectory, "cs"));
csBuilder.setEngineLibraries((List<String>)context.get("engineLibs"));
csBuilder.setOutputDirectory(new File(buildDirectory, extDir.getName()));
csBuilder.setEngineLibraries(libs);
csBuilder.setOutputFile(library);
csBuilder.setOutputName(name);
csBuilder.setPlatform(this.platform);
Expand Down
18 changes: 16 additions & 2 deletions server/src/main/java/com/defold/extender/ExtenderUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -724,15 +724,29 @@ public static String switchExtension(String name, String newExt) {
public static boolean isAppleTarget(String platform) {
return platform.equals("arm64-osx") ||
platform.equals("x86_64-osx") ||
platform.equals("x86_64-ios") ||
platform.equals("arm64-ios");
platform.equals("arm64-ios") ||
platform.equals("x86_64-ios");
}

public static boolean isMacOSTarget(String platform) {
return platform.equals("arm64-osx") ||
platform.equals("x86_64-osx");
}

public static boolean isIOSTarget(String platform) {
return platform.equals("arm64-ios") ||
platform.equals("x86_64-ios");
}

public static boolean isWebTarget(String platform) {
return platform.equals("wasm-web") ||
platform.equals("js-web");
}

public static boolean isWindowsTarget(String platform) {
return platform.equals("x86_64-win32") ||
platform.equals("x86-win32");
}

@SuppressWarnings("unchecked")
public static <V> V get(Object object, String fieldName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

Expand Down Expand Up @@ -91,9 +90,11 @@ public void setSdkProject(File csProject) {
}

private File writeProject() throws IOException {
// The cross compilation (win32) doesn't like absolute unix paths, so we use the path relative to cwd
Path relativeOutputDir = sourceDir.toPath().relativize(outputDir.toPath());

context.put("PINVOKE", engineLibs);
context.put("BUILDDIR_CS", outputDir);
context.put("BUILDDIR_CS", relativeOutputDir);
context.put("DMSDK_CSPROJ", csProject.getAbsolutePath());

String projectText = templateExecutor.execute(this.template, context);
Expand Down Expand Up @@ -125,10 +126,26 @@ private static String convertPlatform(String platform) {
return "unknown_platform";
}

private boolean isWindows(String platform) {
if (platform.endsWith("win32"))
return true;
return false;
private static String getLibName(String platform, String name) {
String prefix = "lib";
String suffix = ".a";
if (ExtenderUtil.isWindowsTarget(platform))
{
prefix = "";
suffix = ".lib";
}
return String.format("%s%s%s", prefix, name, suffix);
}

private static String getObjName(String platform, String name) {
String prefix = "lib";
String suffix = ".o";
if (ExtenderUtil.isWindowsTarget(platform))
{
prefix = "";
suffix = ".obj";
}
return String.format("%s%s%s", prefix, name, suffix);
}

private File runDotnet(File project, String platform) throws IOException, InterruptedException, ExtenderException {
Expand All @@ -145,12 +162,7 @@ private File runDotnet(File project, String platform) throws IOException, Interr
commands.add(cmd);
ProcessExecutor.executeCommands(processExecutor, commands); // in parallel

String libName;
if (isWindows(platform))
libName = String.format("%s.lib", outputName);
else
libName = String.format("lib%s.a", outputName);

String libName = getLibName(platform, outputName);
File csOutput = new File(this.outputDir, csplatform);
File csPublish = new File(csOutput, "publish");
File publishLibrary = new File(csPublish, libName);
Expand All @@ -177,35 +189,56 @@ private static Path getNativePath(String platform) throws IOException {
return Paths.get(NUGET_PACKAGES, String.format("microsoft.netcore.app.runtime.nativeaot.%s/%s/runtimes/%s/native", csplatform, dotnetVersion, csplatform));
}

private static ArrayList<String> makePathsAbsolute(String basePath, ArrayList<String> files) {
ArrayList<String> out = new ArrayList<>();
for (String name : files) {
out.add(Paths.get(basePath, name).toString());
}
return out;
}

private static void addLibFlags(String platform, List<String> linkFlags) throws IOException {
Path aotBase = getNativePath(platform);

linkFlags.add(Paths.get(aotBase.toString(), "libbootstrapperdll.o").toString());
ArrayList<String> paths = new ArrayList<>();

// Note: These libraries are specified with full paths, or the linker will link against the dynamic libraries.
// We want to avoid that hassle for now. Let's do that in a step two.
paths.add(getObjName(platform, "bootstrapperdll"));

// TODO: Do we need a way to toggle these behaviors on/off?
linkFlags.add(Paths.get(aotBase.toString(), "libRuntime.WorkstationGC.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libeventpipe-enabled.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libstandalonegc-enabled.a").toString());
paths.add(getLibName(platform, "Runtime.WorkstationGC"));
paths.add(getLibName(platform, "eventpipe-enabled"));
paths.add(getLibName(platform, "standalonegc-enabled"));

linkFlags.add(Paths.get(aotBase.toString(), "libSystem.Native.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libSystem.IO.Compression.Native.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libSystem.Globalization.Native.a").toString());
String aotSuffix = "";
if (ExtenderUtil.isWindowsTarget(platform))
aotSuffix = ".Aot";
paths.add(getLibName(platform, "System.IO.Compression.Native" + aotSuffix));
paths.add(getLibName(platform, "System.Globalization.Native" + aotSuffix));

if (platform.equals("arm64-osx") || platform.equals("x86_64-osx"))
if (ExtenderUtil.isMacOSTarget(platform))
{
linkFlags.add(Paths.get(aotBase.toString(), "libRuntime.VxsortEnabled.a").toString());

paths.add(getLibName(platform, "System.Native"));
paths.add(getLibName(platform, "Runtime.VxsortEnabled"));
}
else if (platform.equals("arm64-ios") || platform.equals("x86_64-ios"))
else if (ExtenderUtil.isIOSTarget(platform))
{
linkFlags.add(Paths.get(aotBase.toString(), "libstdc++compat.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libSystem.Net.Security.Native.a").toString());
linkFlags.add(Paths.get(aotBase.toString(), "libSystem.Security.Cryptography.Native.Apple.a").toString());
paths.add(getLibName(platform, "System.Native"));
paths.add(getLibName(platform, "stdc++compat"));
paths.add(getLibName(platform, "System.Net.Security.Native"));
paths.add(getLibName(platform, "System.Security.Cryptography.Native.Apple"));
linkFlags.add("-licucore");
}
else if (ExtenderUtil.isWindowsTarget(platform))
{
paths.add(getLibName(platform, "Runtime.VxsortEnabled"));
linkFlags.add("-lbcrypt");
linkFlags.add("-lole32");
linkFlags.add("-ladvapi32");
}

// Note: These libraries are specified with full paths, or the linker will link against the dynamic libraries (macOS)
// We want to avoid that hassle for now. Let's do that in a step two.
linkFlags.addAll(makePathsAbsolute(aotBase.toString(), paths));
}

public static void updateContext(String platform, Map<String, Object> context) throws IOException {
Expand Down
7 changes: 4 additions & 3 deletions server/src/main/resources/template.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
<HybridGlobalization>false</HybridGlobalization>

<!-- In order to build Win32 on Linux -->
<IlcUseEnvironmentalTools>True</IlcUseEnvironmentalTools>
<DisableUnsupportedError>True</DisableUnsupportedError>

<TargetName>$(MSBuildProjectName)</TargetName>
<TargetName Condition="'$(OS)' != 'Windows_NT'">lib$(TargetName)</TargetName>
<StackTraceSupport>true</StackTraceSupport>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<IntermediateOutputPath>{{BUILDDIR_CS}}</IntermediateOutputPath>
<OutputPath>{{BUILDDIR_CS}}</OutputPath>
</PropertyGroup>
<ItemGroup>
<!-- <PackageReference Include="Microsoft.DotNet.ILCompiler" Version="9.0.0" /> -->

{{#PINVOKE}}
<DirectPInvoke Include="{{{.}}}" />
{{/PINVOKE}}
Expand Down

0 comments on commit 0e51936

Please sign in to comment.