diff options
author | Toni Solarin-Sodara <toni.edward@outlook.com> | 2018-03-07 18:09:22 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2018-03-07 18:09:22 +0300 |
commit | 998d168987688abbd14810ee757a8320219e9af8 (patch) | |
tree | 2c535d8b8b11133dc05035b932dd4832616703c8 | |
parent | e7cbc86bf9db0ba4fa547a1ec58ccc2073bbd8a1 (diff) |
Add tests for building native shared and static libraries (#5443)
* 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)
17 files changed, 342 insertions, 25 deletions
diff --git a/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs b/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs index d7460d45a..7f1e67700 100644 --- a/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs +++ b/src/ILCompiler.Compiler/src/Compiler/NativeLibraryInitializerRootProvider.cs @@ -20,10 +20,10 @@ namespace ILCompiler /// </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; diff --git a/src/ILCompiler/src/Program.cs b/src/ILCompiler/src/Program.cs index b86796ad6..e980088c8 100644 --- a/src/ILCompiler/src/Program.cs +++ b/src/ILCompiler/src/Program.cs @@ -88,20 +88,20 @@ namespace ILCompiler 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 @@ -190,7 +190,7 @@ namespace ILCompiler Help(syntax.GetHelpText()); return 1; } - + if (_outputFilePath == null) throw new CommandLineException("Output filename must be specified (/out <file>)"); @@ -242,7 +242,7 @@ namespace ILCompiler 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); @@ -316,12 +316,6 @@ namespace ILCompiler 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) { @@ -347,10 +341,17 @@ namespace ILCompiler 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) diff --git a/tests/src/Simple/SharedLibrary/NativeCallable.cs b/tests/src/Simple/SharedLibrary/NativeCallable.cs new file mode 100644 index 000000000..4a080bfcf --- /dev/null +++ b/tests/src/Simple/SharedLibrary/NativeCallable.cs @@ -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() { } + } +}
\ No newline at end of file diff --git a/tests/src/Simple/SharedLibrary/SharedLibrary.cmd b/tests/src/Simple/SharedLibrary/SharedLibrary.cmd new file mode 100644 index 000000000..2167b8ecf --- /dev/null +++ b/tests/src/Simple/SharedLibrary/SharedLibrary.cmd @@ -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 diff --git a/tests/src/Simple/SharedLibrary/SharedLibrary.cpp b/tests/src/Simple/SharedLibrary/SharedLibrary.cpp new file mode 100644 index 000000000..f61053689 --- /dev/null +++ b/tests/src/Simple/SharedLibrary/SharedLibrary.cpp @@ -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; +} diff --git a/tests/src/Simple/SharedLibrary/SharedLibrary.cs b/tests/src/Simple/SharedLibrary/SharedLibrary.cs new file mode 100644 index 000000000..78fd2c9a1 --- /dev/null +++ b/tests/src/Simple/SharedLibrary/SharedLibrary.cs @@ -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(); + } + } +} diff --git a/tests/src/Simple/SharedLibrary/SharedLibrary.csproj b/tests/src/Simple/SharedLibrary/SharedLibrary.csproj new file mode 100644 index 000000000..5bfa784b9 --- /dev/null +++ b/tests/src/Simple/SharedLibrary/SharedLibrary.csproj @@ -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) @"$(NativeIntermediateOutputPath)SharedLibrary.cl.rsp"" Condition="'$(OS)' == 'Windows_NT'" /> + </Target> + + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SimpleTest.targets))\SimpleTest.targets" /> + +</Project> diff --git a/tests/src/Simple/SharedLibrary/SharedLibrary.sh b/tests/src/Simple/SharedLibrary/SharedLibrary.sh new file mode 100755 index 000000000..6640f629f --- /dev/null +++ b/tests/src/Simple/SharedLibrary/SharedLibrary.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +$1/$2 +if [ $? == 100 ]; then + echo pass + exit 0 +else + echo fail + exit 1 +fi diff --git a/tests/src/Simple/SharedLibrary/no_cpp b/tests/src/Simple/SharedLibrary/no_cpp new file mode 100644 index 000000000..639bcaf8b --- /dev/null +++ b/tests/src/Simple/SharedLibrary/no_cpp @@ -0,0 +1 @@ +Skip this test for cpp codegen mode diff --git a/tests/src/Simple/SharedLibrary/no_linux b/tests/src/Simple/SharedLibrary/no_linux new file mode 100644 index 000000000..872fb9d56 --- /dev/null +++ b/tests/src/Simple/SharedLibrary/no_linux @@ -0,0 +1 @@ +Skip this test for linux diff --git a/tests/src/Simple/StaticLibrary/NativeCallable.cs b/tests/src/Simple/StaticLibrary/NativeCallable.cs new file mode 100644 index 000000000..a96a6c793 --- /dev/null +++ b/tests/src/Simple/StaticLibrary/NativeCallable.cs @@ -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() { } + } +} diff --git a/tests/src/Simple/StaticLibrary/StaticLibrary.cmd b/tests/src/Simple/StaticLibrary/StaticLibrary.cmd new file mode 100644 index 000000000..2167b8ecf --- /dev/null +++ b/tests/src/Simple/StaticLibrary/StaticLibrary.cmd @@ -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 diff --git a/tests/src/Simple/StaticLibrary/StaticLibrary.cpp b/tests/src/Simple/StaticLibrary/StaticLibrary.cpp new file mode 100644 index 000000000..25674cf25 --- /dev/null +++ b/tests/src/Simple/StaticLibrary/StaticLibrary.cpp @@ -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; +} diff --git a/tests/src/Simple/StaticLibrary/StaticLibrary.cs b/tests/src/Simple/StaticLibrary/StaticLibrary.cs new file mode 100644 index 000000000..3e7f6e78c --- /dev/null +++ b/tests/src/Simple/StaticLibrary/StaticLibrary.cs @@ -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; + } + } +} diff --git a/tests/src/Simple/StaticLibrary/StaticLibrary.csproj b/tests/src/Simple/StaticLibrary/StaticLibrary.csproj new file mode 100644 index 000000000..feaf7f37f --- /dev/null +++ b/tests/src/Simple/StaticLibrary/StaticLibrary.csproj @@ -0,0 +1,52 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + + <PropertyGroup> + <OutputType>Library</OutputType> + <NativeLib>Static</NativeLib> + </PropertyGroup> + + <ItemGroup> + <Compile Include="*.cs" /> + </ItemGroup> + + <Target Name="NativeRunnerCompile" AfterTargets="LinkNative"> + <PropertyGroup> + <NativeRunnerBinary>$(NativeOutputPath)StaticLibrary</NativeRunnerBinary> + </PropertyGroup> + + <ItemGroup> + <CppCompile Include="StaticLibrary.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'" /> + <NativeRunnerCompilerArg Include="$(NativeBinary)" Condition="'$(OS)' == 'Windows_NT'" /> + <NativeRunnerCompilerArg Include="@(NativeLibrary)" Condition="'$(OS)' == 'Windows_NT'" /> + </ItemGroup> + + <ItemGroup> + <NativeRunnerLinkerArg Include="@(NativeLibrary)" /> + <NativeRunnerLinkerArg Include="-g" /> + <NativeRunnerLinkerArg Include="-Wl,-rpath,'$ORIGIN'" /> + <NativeRunnerLinkerArg Include="-pthread" /> + <NativeRunnerLinkerArg Include="-lstdc++" /> + <NativeRunnerLinkerArg Include="-ldl" /> + <NativeRunnerLinkerArg Include="-lm" /> + <NativeRunnerLinkerArg Include="-lcurl" /> + <NativeRunnerLinkerArg Include="-lz" /> + <NativeRunnerLinkerArg Include="-luuid" Condition="'$(TargetOS)' != 'OSX'" /> + <NativeRunnerLinkerArg Include="-lrt" Condition="'$(TargetOS)' != 'OSX'" /> + <NativeRunnerLinkerArg Include="-licucore" Condition="'$(TargetOS)' == 'OSX'" /> + </ItemGroup> + + <Exec Command="$(CppCompiler) @(NativeRunnerCompilerArg, ' ') $(NativeBinary) @(NativeRunnerLinkerArg, ' ')" Condition="'$(OS)' != 'Windows_NT'" /> + <WriteLinesToFile File="$(NativeIntermediateOutputPath)SharedLibrary.cl.rsp" Lines="@(NativeRunnerCompilerArg)" Overwrite="true" Condition="'$(OS)' == 'Windows_NT'"/> + <Exec Command="$(CppCompiler) @"$(NativeIntermediateOutputPath)SharedLibrary.cl.rsp"" Condition="'$(OS)' == 'Windows_NT'" /> + </Target> + + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), SimpleTest.targets))\SimpleTest.targets" /> + +</Project> diff --git a/tests/src/Simple/StaticLibrary/StaticLibrary.sh b/tests/src/Simple/StaticLibrary/StaticLibrary.sh new file mode 100755 index 000000000..6640f629f --- /dev/null +++ b/tests/src/Simple/StaticLibrary/StaticLibrary.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +$1/$2 +if [ $? == 100 ]; then + echo pass + exit 0 +else + echo fail + exit 1 +fi diff --git a/tests/src/Simple/StaticLibrary/no_cpp b/tests/src/Simple/StaticLibrary/no_cpp new file mode 100644 index 000000000..639bcaf8b --- /dev/null +++ b/tests/src/Simple/StaticLibrary/no_cpp @@ -0,0 +1 @@ +Skip this test for cpp codegen mode |