Skip to content

Commit

Permalink
Use the system linker for linking symbols libraries outside Windows.
Browse files Browse the repository at this point in the history
Builtin lld is giving some weird linking errors when linking with the
new LLVM version. We probably need to set some custom options. Using the
system linker should be a better idea anyway, more robust and future
proof.
  • Loading branch information
tritao committed Sep 29, 2023
1 parent 7c87f29 commit c045dfd
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 16 deletions.
3 changes: 1 addition & 2 deletions src/CppParser/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4853,8 +4853,7 @@ ParserResult* Parser::Compile(const std::string& File)
const llvm::Triple Triple = c->getTarget().getTriple();
llvm::StringRef Dir(llvm::sys::path::parent_path(File));
llvm::SmallString<1024> Object(Dir);
llvm::sys::path::append(Object,
(Triple.isOSWindows() ? "" : "lib") + Stem + ".o");
llvm::sys::path::append(Object, Stem + ".o");
c->getFrontendOpts().OutputFile = std::string(Object);

llvm::LLVMContext context;
Expand Down
73 changes: 60 additions & 13 deletions src/Generator/Passes/GenerateSymbolsPass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,26 +63,73 @@ private void GenerateSymbols()
new[] { module }).SelectMany(d => d.Libraries))
linkerOptions.AddLibraries(library);

using (var result = Parser.ClangParser.Build(
Context.ParserOptions, linkerOptions, path,
Last: remainingCompilationTasks == 1))
{
if (PrintDiagnostics(result))
{
compiledLibraries[module] = new CompiledLibrary
{
OutputDir = Options.OutputDir,
Library = module.SymbolsLibraryName
};
}
}
compiledLibraries[module] = Build(linkerOptions, path, module);
}
}

RemainingCompilationTasks--;
}
}

private CompiledLibrary Build(LinkerOptions linkerOptions, string path, Module module)
{
var useBuiltinToolchain = Platform.IsWindows;
if (useBuiltinToolchain)
{
using var result = Parser.ClangParser.Build(
Context.ParserOptions, linkerOptions, path,
Last: remainingCompilationTasks == 1);

if (!PrintDiagnostics(result))
return null;
}
else
{
using var result = Parser.ClangParser.Compile(Context.ParserOptions, path);
if (result != null)
{
if (!PrintDiagnostics(result))
return null;
}

var targetPlatform = Options.Compilation.Platform.GetValueOrDefault(Platform.Host);
var linker = LinkerOptions.GetLinkerExecutableName(targetPlatform);

linkerOptions.AddArguments("-L" + Path.GetDirectoryName(path));
linkerOptions.SetupLibraryArguments();

var objectFile = Path.ChangeExtension(path, "o");
linkerOptions.AddArguments(objectFile);

var sharedObjectFile = LinkerOptions.GetSharedObjectName(path, targetPlatform);
linkerOptions.AddArguments("-o " + sharedObjectFile);

var invocation = linkerOptions.GetLinkerInvocation();

Diagnostics.Message($"Linking library {Path.GetFileName(sharedObjectFile)}...");
if (Options.Verbose)
Diagnostics.Message($"Invoking the linker with: {linker} {invocation}");

var outMessage = ProcessHelper.Run(
linker, invocation, out var errorCode, out var errorMessage);

if (errorCode != 0)
{
Diagnostics.Error($"Linking failed with: {outMessage} {errorMessage}");
}
else
{
Diagnostics.Message($"Linking success.");
}
}

return new CompiledLibrary
{
OutputDir = Options.OutputDir,
Library = module.SymbolsLibraryName
};
}

public override bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization)
{
if (!specialization.IsGenerated ||
Expand Down
152 changes: 151 additions & 1 deletion src/Parser/LinkerOptions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace CppSharp.Parser
{
Expand Down Expand Up @@ -70,5 +74,151 @@ public void Setup(string triple, LanguageVersion? languageVersion)
break;
}
}

public void SetupLibraryArguments()
{
for (uint i = 0; i < LibraryDirsCount; i++)
{
var dir = GetLibraryDirs(i);
AddArguments("-L" + dir);
}

for (uint i = 0; i < LibrariesCount; i++)
{
var lib = GetLibraries(i);
AddArguments("-l" + lib);
}
}

public string GetLinkerInvocation()
{
var args = new List<string>();
for (uint i = 0; i < ArgumentsCount; i++)
{
var arg = GetArguments(i);
args.Add(arg);
}

return string.Join(" ", args);
}

public static string GetSharedObjectName(string path, TargetPlatform targetPlatform)
{
var prefix = GetPlatformSharedObjectPrefix(targetPlatform);
var extension = GetPlatformSharedObjectExtension(targetPlatform);
var name = $"{prefix}{Path.GetFileNameWithoutExtension(path)}.{extension}";
return Path.Join(Path.GetDirectoryName(path), name);
}

public static string GetLinkerExecutableName(TargetPlatform targetPlatform)
{
// If LLD exists on the PATH, then prefer it. If not, use the host linker.
var lldLinkerExe = GetLLDLinkerExecutableName(targetPlatform);
return ExistsOnPath(lldLinkerExe) ?
lldLinkerExe : GetPlatformLinkerExecutableName(targetPlatform);
}

public static string GetPlatformSharedObjectPrefix(TargetPlatform targetPlatform)
{
switch (targetPlatform)
{
case TargetPlatform.Windows:
return "";
case TargetPlatform.Linux:
case TargetPlatform.Android:
case TargetPlatform.MacOS:
case TargetPlatform.iOS:
case TargetPlatform.WatchOS:
case TargetPlatform.TVOS:
case TargetPlatform.Emscripten:
return "lib";
default:
throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null);
}
}

public static string GetPlatformSharedObjectExtension(TargetPlatform targetPlatform)
{
switch (targetPlatform)
{
case TargetPlatform.Windows:
return "dll";
case TargetPlatform.Linux:
case TargetPlatform.Android:
case TargetPlatform.MacOS:
case TargetPlatform.iOS:
case TargetPlatform.WatchOS:
case TargetPlatform.TVOS:
case TargetPlatform.Emscripten:
return "so";
default:
throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null);
}
}

public static string GetPlatformLinkerExecutableName(TargetPlatform targetPlatform)
{
switch (targetPlatform)
{
case TargetPlatform.Windows:
return "link.exe";
case TargetPlatform.Linux:
return "ld";
case TargetPlatform.Android:
case TargetPlatform.MacOS:
case TargetPlatform.iOS:
case TargetPlatform.WatchOS:
case TargetPlatform.TVOS:
case TargetPlatform.Emscripten:
return GetLLDLinkerExecutableName(targetPlatform);
default:
throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null);
}
}

public static string GetLLDLinkerExecutableName(TargetPlatform targetPlatform)
{
switch (targetPlatform)
{
case TargetPlatform.Windows:
return "lld-link";
case TargetPlatform.Linux:
case TargetPlatform.Android:
return "ld.lld";
case TargetPlatform.MacOS:
case TargetPlatform.iOS:
case TargetPlatform.WatchOS:
case TargetPlatform.TVOS:
return "ld64.lld";
case TargetPlatform.Emscripten:
return "wasm-ld";
default:
throw new ArgumentOutOfRangeException(nameof(targetPlatform), targetPlatform, null);
}
}

private static bool ExistsOnPath(string fileName)
{
return GetFullPath(fileName) != null;
}

private static string GetFullPath(string fileName)
{
if (fileName == null) throw new ArgumentNullException(nameof(fileName));
if (File.Exists(fileName))
return Path.GetFullPath(fileName);

var environmentVariablePath = Environment.GetEnvironmentVariable("PATH");
if (environmentVariablePath == null)
throw new NullReferenceException(nameof(environmentVariablePath));

foreach (var path in environmentVariablePath.Split(Path.PathSeparator))
{
var fullPath = Path.Combine(path, fileName);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
}
}

0 comments on commit c045dfd

Please sign in to comment.