Skip to content
This repository has been archived by the owner on Nov 1, 2020. It is now read-only.

Commit

Permalink
Add tests for building native shared and static libraries (#5443)
Browse files Browse the repository at this point in the history
* add preliminary Library test project (#4985)

* update library test and make it pass on Windows (#4985)

* rename Library test project to SharedLibrary (#4985)

* update sharedlib test, make it pass on darwin (#4985)

* update msbuild tag names (#4985)

* move native runner rsp file to native intermediate output path (#4985)

* add test for static library, make it pass on unix (#4985)

* update static library test, make it pass on Windows

* skip shared library test for linux (#4985)

* fix StaticLibrary linux build (#4985)

* update unix linker args when building StaticLibrary native binary (#4985)

* add copyright headers to all newly added source files (#4985)

* fix multimodule build errors for native library builds (#4985)

* add comment explaining owning module of native library startup method (#4985)

* set owning module of native library startup method to gen'd module (#4985)

* remove unneeded System.Linq import (#4985)
  • Loading branch information
tonerdo authored and jkotas committed Mar 7, 2018
1 parent e7cbc86 commit 998d168
Show file tree
Hide file tree
Showing 17 changed files with 342 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ public class NativeLibraryInitializerRootProvider : ICompilationRootProvider
/// </summary>
public const string ManagedEntryPointMethodName = "__managed__Startup";

private EcmaModule _module;
private ModuleDesc _module;
private IList<MethodDesc> _libraryInitializers;

public NativeLibraryInitializerRootProvider(EcmaModule module, IList<MethodDesc> libraryInitializers)
public NativeLibraryInitializerRootProvider(ModuleDesc module, IList<MethodDesc> libraryInitializers)
{
_module = module;
_libraryInitializers = libraryInitializers;
Expand Down
47 changes: 24 additions & 23 deletions src/ILCompiler/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,20 @@ private void InitializeDefaultOptions()

switch (RuntimeInformation.ProcessArchitecture)
{
case Architecture.X86:
_targetArchitecture = TargetArchitecture.X86;
break;
case Architecture.X64:
_targetArchitecture = TargetArchitecture.X64;
break;
case Architecture.Arm:
_targetArchitecture = TargetArchitecture.ARM;
break;
case Architecture.Arm64:
_targetArchitecture = TargetArchitecture.ARM64;
break;
default:
throw new NotImplementedException();
case Architecture.X86:
_targetArchitecture = TargetArchitecture.X86;
break;
case Architecture.X64:
_targetArchitecture = TargetArchitecture.X64;
break;
case Architecture.Arm:
_targetArchitecture = TargetArchitecture.ARM;
break;
case Architecture.Arm64:
_targetArchitecture = TargetArchitecture.ARM64;
break;
default:
throw new NotImplementedException();
}

// Workaround for https://github.com/dotnet/corefx/issues/25267
Expand Down Expand Up @@ -190,7 +190,7 @@ private int Run(string[] args)
Help(syntax.GetHelpText());
return 1;
}

if (_outputFilePath == null)
throw new CommandLineException("Output filename must be specified (/out <file>)");

Expand Down Expand Up @@ -242,7 +242,7 @@ private int Run(string[] args)
SharedGenericsMode.CanonicalReferenceTypes : SharedGenericsMode.Disabled;

// TODO: compiler switch for SIMD support?
var simdVectorLength = (_isCppCodegen || _isWasmCodegen) ? SimdVectorLength.None : SimdVectorLength.Vector128Bit;
var simdVectorLength = (_isCppCodegen || _isWasmCodegen) ? SimdVectorLength.None : SimdVectorLength.Vector128Bit;
var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, TargetAbi.CoreRT, simdVectorLength);
var typeSystemContext = new CompilerTypeSystemContext(targetDetails, genericsMode);

Expand Down Expand Up @@ -316,12 +316,6 @@ private int Run(string[] args)
new LibraryInitializers(typeSystemContext, _isCppCodegen);
compilationRoots.Add(new MainMethodRootProvider(entrypointModule, libraryInitializers.LibraryInitializerMethods));
}
else if (_nativeLib)
{
EcmaModule module = (EcmaModule)typeSystemContext.SystemModule;
LibraryInitializers libraryInitializers = new LibraryInitializers(typeSystemContext, _isCppCodegen);
compilationRoots.Add(new NativeLibraryInitializerRootProvider(module, libraryInitializers.LibraryInitializerMethods));
}

if (_multiFile)
{
Expand All @@ -347,10 +341,17 @@ private int Run(string[] args)
throw new Exception("No entrypoint module");

compilationRoots.Add(new ExportedMethodsRootProvider((EcmaModule)typeSystemContext.SystemModule));

compilationGroup = new SingleFileCompilationModuleGroup(typeSystemContext);
}

if (_nativeLib)
{
// Set owning module of generated native library startup method to compiler generated module,
// to ensure the startup method is included in the object file during multimodule mode build
LibraryInitializers libraryInitializers = new LibraryInitializers(typeSystemContext, _isCppCodegen);
compilationRoots.Add(new NativeLibraryInitializerRootProvider(compilationGroup.GeneratedAssembly, libraryInitializers.LibraryInitializerMethods));
}

if (_rdXmlFilePaths.Count > 0)
Console.WriteLine("Warning: RD.XML processing will change before release (https://github.com/dotnet/corert/issues/5001)");
foreach (var rdXmlFilePath in _rdXmlFilePaths)
Expand Down
14 changes: 14 additions & 0 deletions tests/src/Simple/SharedLibrary/NativeCallable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class NativeCallableAttribute : Attribute
{
public string EntryPoint;
public CallingConvention CallingConvention;
public NativeCallableAttribute() { }
}
}
12 changes: 12 additions & 0 deletions tests/src/Simple/SharedLibrary/SharedLibrary.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@echo off
setlocal
"%1\%2"
set ErrorCode=%ERRORLEVEL%
IF "%ErrorCode%"=="100" (
echo %~n0: pass
EXIT /b 0
) ELSE (
echo %~n0: fail - %ErrorCode%
EXIT /b 1
)
endlocal
66 changes: 66 additions & 0 deletions tests/src/Simple/SharedLibrary/SharedLibrary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#ifdef _WIN32
#include "windows.h"
#else
#include "dlfcn.h"
#endif
#include "stdio.h"
#include "string.h"

#ifndef _WIN32
#define __stdcall
#endif

// typedef for shared lib exported methods
typedef int(__stdcall *f_ReturnsPrimitiveInt)();
typedef bool(__stdcall *f_ReturnsPrimitiveBool)();
typedef char(__stdcall *f_ReturnsPrimitiveChar)();
typedef void(__stdcall *f_EnsureManagedClassLoaders)();

#ifdef _WIN32
int main()
#else
int main(int argc, char* argv[])
#endif
{
#ifdef _WIN32
HINSTANCE handle = LoadLibrary("SharedLibrary.dll");
#elif __APPLE__
void *handle = dlopen(strcat(argv[0], ".dylib"), RTLD_LAZY);
#else
void *handle = dlopen(strcat(argv[0], ".so"), RTLD_LAZY);
#endif

if (!handle)
return 1;

#ifdef _WIN32
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)GetProcAddress(handle, "ReturnsPrimitiveInt");
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)GetProcAddress(handle, "ReturnsPrimitiveBool");
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)GetProcAddress(handle, "ReturnsPrimitiveChar");
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)GetProcAddress(handle, "EnsureManagedClassLoaders");
#else
f_ReturnsPrimitiveInt returnsPrimitiveInt = (f_ReturnsPrimitiveInt)dlsym(handle, "ReturnsPrimitiveInt");
f_ReturnsPrimitiveBool returnsPrimitiveBool = (f_ReturnsPrimitiveBool)dlsym(handle, "ReturnsPrimitiveBool");
f_ReturnsPrimitiveChar returnsPrimitiveChar = (f_ReturnsPrimitiveChar)dlsym(handle, "ReturnsPrimitiveChar");
f_EnsureManagedClassLoaders ensureManagedClassLoaders = (f_EnsureManagedClassLoaders)dlsym(handle, "EnsureManagedClassLoaders");
#endif

if (returnsPrimitiveInt() != 10)
return 1;

if (!returnsPrimitiveBool())
return 1;

if (returnsPrimitiveChar() != 'a')
return 1;

// As long as no unmanaged exception is thrown
// managed class loaders were initialized successfully
ensureManagedClassLoaders();

return 100;
}
37 changes: 37 additions & 0 deletions tests/src/Simple/SharedLibrary/SharedLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

namespace SharedLibrary
{
public class ClassLibrary
{
[NativeCallable(EntryPoint = "ReturnsPrimitiveInt", CallingConvention = CallingConvention.StdCall)]
public static int ReturnsPrimitiveInt()
{
return 10;
}

[NativeCallable(EntryPoint = "ReturnsPrimitiveBool", CallingConvention = CallingConvention.StdCall)]
public static bool ReturnsPrimitiveBool()
{
return true;
}

[NativeCallable(EntryPoint = "ReturnsPrimitiveChar", CallingConvention = CallingConvention.StdCall)]
public static char ReturnsPrimitiveChar()
{
return 'a';
}

[NativeCallable(EntryPoint = "EnsureManagedClassLoaders", CallingConvention = CallingConvention.StdCall)]
public static void EnsureManagedClassLoaders()
{
Random random = new Random();
random.Next();
}
}
}
35 changes: 35 additions & 0 deletions tests/src/Simple/SharedLibrary/SharedLibrary.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<OutputType>Library</OutputType>
<NativeLib>Shared</NativeLib>
</PropertyGroup>

<ItemGroup>
<Compile Include="*.cs" />
</ItemGroup>

<Target Name="NativeRunnerCompile" AfterTargets="LinkNative">
<PropertyGroup>
<NativeRunnerBinary>$(NativeOutputPath)SharedLibrary</NativeRunnerBinary>
</PropertyGroup>

<ItemGroup>
<CppCompile Include="SharedLibrary.cpp" />
</ItemGroup>

<ItemGroup>
<NativeRunnerCompilerArg Include="@(CppCompile)" />
<NativeRunnerCompilerArg Include="-o $(NativeRunnerBinary)" Condition="'$(OS)' != 'Windows_NT'" />
<NativeRunnerCompilerArg Include="/Fo$(NativeRunnerBinary)" Condition="'$(OS)' == 'Windows_NT'" />
<NativeRunnerCompilerArg Include="/Fe$(NativeRunnerBinary)" Condition="'$(OS)' == 'Windows_NT'" />
</ItemGroup>

<Exec Command="$(CppCompiler) @(NativeRunnerCompilerArg, ' ')" Condition="'$(OS)' != 'Windows_NT'" />
<WriteLinesToFile File="$(NativeIntermediateOutputPath)SharedLibrary.cl.rsp" Lines="@(NativeRunnerCompilerArg)" Overwrite="true" Condition="'$(OS)' == 'Windows_NT'"/>
<Exec Command="$(CppCompiler) @&quot;$(NativeIntermediateOutputPath)SharedLibrary.cl.rsp&quot;" Condition="'$(OS)' == 'Windows_NT'" />
</Target>

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SimpleTest.targets))\SimpleTest.targets" />

</Project>
9 changes: 9 additions & 0 deletions tests/src/Simple/SharedLibrary/SharedLibrary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
$1/$2
if [ $? == 100 ]; then
echo pass
exit 0
else
echo fail
exit 1
fi
1 change: 1 addition & 0 deletions tests/src/Simple/SharedLibrary/no_cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Skip this test for cpp codegen mode
1 change: 1 addition & 0 deletions tests/src/Simple/SharedLibrary/no_linux
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Skip this test for linux
14 changes: 14 additions & 0 deletions tests/src/Simple/StaticLibrary/NativeCallable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Runtime.InteropServices
{
[AttributeUsage(AttributeTargets.Method)]
public sealed class NativeCallableAttribute : Attribute
{
public string EntryPoint;
public CallingConvention CallingConvention;
public NativeCallableAttribute() { }
}
}
12 changes: 12 additions & 0 deletions tests/src/Simple/StaticLibrary/StaticLibrary.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@echo off
setlocal
"%1\%2"
set ErrorCode=%ERRORLEVEL%
IF "%ErrorCode%"=="100" (
echo %~n0: pass
EXIT /b 0
) ELSE (
echo %~n0: fail - %ErrorCode%
EXIT /b 1
)
endlocal
23 changes: 23 additions & 0 deletions tests/src/Simple/StaticLibrary/StaticLibrary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#include "stdio.h"

extern "C" int Add(int a, int b);
extern "C" int Subtract(int a, int b);
extern "C" bool Not(bool b);

int main()
{
if (Add(2, 3) != 5)
return 1;

if (Subtract(3, 1) != 2)
return 1;

if (!Not(false))
return 1;

return 100;
}
30 changes: 30 additions & 0 deletions tests/src/Simple/StaticLibrary/StaticLibrary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

namespace StaticLibrary
{
public class ClassLibrary
{
[NativeCallable(EntryPoint = "Add", CallingConvention = CallingConvention.StdCall)]
public static int Add(int a, int b)
{
return a + b;
}

[NativeCallable(EntryPoint = "Subtract", CallingConvention = CallingConvention.StdCall)]
public static int Subtract(int a, int b)
{
return a - b;
}

[NativeCallable(EntryPoint = "Not", CallingConvention = CallingConvention.StdCall)]
public static bool Not(bool b)
{
return !b;
}
}
}
Loading

0 comments on commit 998d168

Please sign in to comment.