mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 00:20:40 +03:00
Merge remote-tracking branch 'origin/master' into merge-2.104
This commit is contained in:
commit
b97ae0340d
23 changed files with 606 additions and 55 deletions
|
@ -67,7 +67,7 @@ common_steps_template: &COMMON_STEPS_TEMPLATE
|
||||||
excludes+='|^druntime-test-exceptions-debug$'
|
excludes+='|^druntime-test-exceptions-debug$'
|
||||||
elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then
|
elif [[ "$CI_OS-$CI_ARCH" == "osx-arm64" ]]; then
|
||||||
# FIXME: sporadic segfaults/bus errors with enabled optimizations
|
# FIXME: sporadic segfaults/bus errors with enabled optimizations
|
||||||
excludes+='|^core.thread.fiber-shared$'
|
excludes+='|^core.thread.fiber(-shared)?$'
|
||||||
fi
|
fi
|
||||||
ctest -j$PARALLELISM --output-on-failure -E "$excludes"
|
ctest -j$PARALLELISM --output-on-failure -E "$excludes"
|
||||||
|
|
||||||
|
|
2
.github/actions/3-build-cross/action.yml
vendored
2
.github/actions/3-build-cross/action.yml
vendored
|
@ -145,4 +145,4 @@ runs:
|
||||||
${{ inputs.cmake_flags }}
|
${{ inputs.cmake_flags }}
|
||||||
${{ inputs.with_pgo == 'true' && '-DDFLAGS_LDC=-fprofile-use=../pgo-ldc/merged.profdata' || '' }}
|
${{ inputs.with_pgo == 'true' && '-DDFLAGS_LDC=-fprofile-use=../pgo-ldc/merged.profdata' || '' }}
|
||||||
${{ env.CROSS_CMAKE_FLAGS }}
|
${{ env.CROSS_CMAKE_FLAGS }}
|
||||||
build_targets: ldc2 ldmd2 ldc-build-runtime ldc-profdata ldc-prune-cache timetrace2txt
|
build_targets: ldc2 ldmd2 ldc-build-runtime ldc-build-plugin ldc-profdata ldc-prune-cache timetrace2txt
|
||||||
|
|
1
.github/actions/5-install/action.yml
vendored
1
.github/actions/5-install/action.yml
vendored
|
@ -22,6 +22,7 @@ runs:
|
||||||
else
|
else
|
||||||
mkdir -p install/bin
|
mkdir -p install/bin
|
||||||
cp build-cross/bin/{ldc2,ldmd2,ldc-build-runtime,ldc-profdata,ldc-prune-cache,timetrace2txt} install/bin/
|
cp build-cross/bin/{ldc2,ldmd2,ldc-build-runtime,ldc-profdata,ldc-prune-cache,timetrace2txt} install/bin/
|
||||||
|
cp build-cross/bin/ldc-build-plugin install/bin/ || true
|
||||||
cp -R build-cross-libs/lib install/
|
cp -R build-cross-libs/lib install/
|
||||||
cp build-cross/lib/{libldc_rt.*,libLTO-ldc.dylib,LLVMgold-ldc.so} install/lib/ || true
|
cp build-cross/lib/{libldc_rt.*,libLTO-ldc.dylib,LLVMgold-ldc.so} install/lib/ || true
|
||||||
mkdir install/etc
|
mkdir install/etc
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,13 +1,25 @@
|
||||||
# LDC master
|
# LDC master
|
||||||
|
|
||||||
|
#### Big news
|
||||||
|
|
||||||
|
#### Platform support
|
||||||
|
|
||||||
|
#### Bug fixes
|
||||||
|
|
||||||
|
# LDC 1.33.0 (2023-07-23)
|
||||||
|
|
||||||
#### Big news
|
#### Big news
|
||||||
- Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345)
|
- Frontend, druntime and Phobos are at version [2.103.1](https://dlang.org/changelog/2.103.0.html), incl. new command-line option `-verror-supplements`. (#4345)
|
||||||
|
- The `--plugin` commandline option now also accepts semantic analysis plugins. Semantic analysis plugins are recognized by exporting the symbol: `extern(C) void runSemanticAnalysis(Module m)`. The plugin's `runSemanticAnalysis` function is called for each module, after all other semantic analysis steps (also after DCompute SemA), just before object codegen. (#4430)
|
||||||
|
- New tool `ldc-build-plugin` that helps compiling user plugins. It downloads the correct LDC source version (if it's not already available), and calls LDC with the correct commandline flags to build a plugin. (#4430)
|
||||||
- New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395)
|
- New commandline option `-femit-local-var-lifetime` that enables variable lifetime (scope) annotation to LLVM IR codegen. Lifetime annotation enables stack memory reuse for local variables with non-overlapping scope. (#4395)
|
||||||
- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417)
|
- C files are now automatically preprocessed using the external C compiler (configurable via `-gcc` or the `CC` environment variable, and `-Xcc` for extra flags). Extra preprocessor flags (e.g., include dirs and manual defines) can be added via new command-line option `-P`. (#4417)
|
||||||
- Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing).
|
- Windows: If `clang-cl.exe` is on `PATH`, it is preferred over Microsoft's `cl.exe` by default (e.g., to avoid printing the C source file name to stderr during preprocessing).
|
||||||
- Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420)
|
- Less pedantic checks for conflicting C(++) function declarations when compiling multiple modules to a single object file ('Error: Function type does not match previously declared function with the same mangled name'). The error now only appears if an object file actually references multiple conflicting functions. (#4420)
|
||||||
|
- New command-line option `--fcf-protection`, which enables Intel's Control-Flow Enforcement Technology (CET). (#4437)
|
||||||
|
|
||||||
#### Platform support
|
#### Platform support
|
||||||
|
- Supports LLVM 9.0 - 15.0.
|
||||||
|
|
||||||
#### Bug fixes
|
#### Bug fixes
|
||||||
- Handle potential lambda mangle collisions across separately compiled object files (and the linker then silently picking an arbitrary implementation). Lambdas (and their nested global variables) are now internal to each referencing object file (`static` linkage in C). (#4415)
|
- Handle potential lambda mangle collisions across separately compiled object files (and the linker then silently picking an arbitrary implementation). Lambdas (and their nested global variables) are now internal to each referencing object file (`static` linkage in C). (#4415)
|
||||||
|
|
|
@ -249,17 +249,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT WIN32 AND NOT CYGWIN)
|
|
||||||
# Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global
|
|
||||||
# weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by
|
|
||||||
# different translation units being compiled with different visibility settings."
|
|
||||||
# See LLVM's cmake/modules/HandleLLVMOptions.cmake.
|
|
||||||
check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG)
|
|
||||||
if (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG})
|
|
||||||
append("-fvisibility-inlines-hidden" LDC_CXXFLAGS)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
# Remove flags here, for exceptions and RTTI.
|
# Remove flags here, for exceptions and RTTI.
|
||||||
# CL.EXE complains to override flags like "/GR /GR-".
|
# CL.EXE complains to override flags like "/GR /GR-".
|
||||||
|
@ -683,10 +672,26 @@ if(LDC_ENABLE_PLUGINS)
|
||||||
|
|
||||||
if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG)
|
if(LINKER_ACCEPTS_EXPORT_DYNAMIC_FLAG)
|
||||||
set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic")
|
set(LDC_LINKERFLAG_LIST "${LDC_LINKERFLAG_LIST};-Wl,--export-dynamic")
|
||||||
|
else()
|
||||||
|
message(WARNING "Linker does not accept --export-dynamic, user plugins may give missing symbol errors upon load")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}")
|
message(STATUS "-- Building LDC with plugin support (LDC_ENABLE_PLUGINS): ${LDC_ENABLE_PLUGINS}")
|
||||||
|
message(STATUS "-- Linking LDC with flags: ${ALTERNATIVE_MALLOC_O};${LDC_LINKERFLAG_LIST}")
|
||||||
|
|
||||||
|
if(NOT WIN32 AND NOT CYGWIN)
|
||||||
|
# Unify symbol visibility with LLVM to silence linker warning "direct access in function X to global
|
||||||
|
# weak symbol Y means the weak symbol cannot be overridden at runtime. This was likely caused by
|
||||||
|
# different translation units being compiled with different visibility settings."
|
||||||
|
# See LLVM's cmake/modules/HandleLLVMOptions.cmake.
|
||||||
|
check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG)
|
||||||
|
if (LDC_ENABLE_PLUGINS AND NOT APPLE)
|
||||||
|
# For plugins, we shouldn't apply this flag because it hides the inline methods of e.g. Visitor. On macOS it's OK to add.
|
||||||
|
elseif (${SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG})
|
||||||
|
append("-fvisibility-inlines-hidden" LDC_CXXFLAGS)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
build_d_executable(
|
build_d_executable(
|
||||||
"${LDC_EXE}"
|
"${LDC_EXE}"
|
||||||
|
|
|
@ -25,6 +25,7 @@ endmacro()
|
||||||
# - DFLAGS_BASE
|
# - DFLAGS_BASE
|
||||||
# - LDC_LINK_MANUALLY
|
# - LDC_LINK_MANUALLY
|
||||||
# - D_LINKER_ARGS
|
# - D_LINKER_ARGS
|
||||||
|
# - LDC_ENABLE_PLUGINS
|
||||||
function(build_d_executable target_name output_exe d_src_files compiler_args linker_args extra_compile_deps link_deps compile_separately)
|
function(build_d_executable target_name output_exe d_src_files compiler_args linker_args extra_compile_deps link_deps compile_separately)
|
||||||
set(dflags "${D_COMPILER_FLAGS} ${DFLAGS_BASE} ${compiler_args}")
|
set(dflags "${D_COMPILER_FLAGS} ${DFLAGS_BASE} ${compiler_args}")
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
@ -40,7 +41,9 @@ function(build_d_executable target_name output_exe d_src_files compiler_args lin
|
||||||
# Compile all D modules to a single object.
|
# Compile all D modules to a single object.
|
||||||
set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}${CMAKE_CXX_OUTPUT_EXTENSION})
|
set(object_file ${PROJECT_BINARY_DIR}/obj/${target_name}${CMAKE_CXX_OUTPUT_EXTENSION})
|
||||||
# Default to -linkonce-templates with LDMD host compiler, to speed-up optimization.
|
# Default to -linkonce-templates with LDMD host compiler, to speed-up optimization.
|
||||||
if("${D_COMPILER_ID}" STREQUAL "LDMD")
|
if("${target_name}" STREQUAL "ldc2" AND LDC_ENABLE_PLUGINS)
|
||||||
|
# For plugin support we need ldc2's symbols to be global, don't use -linkonce-templates.
|
||||||
|
elseif("${D_COMPILER_ID}" STREQUAL "LDMD")
|
||||||
set(dflags -linkonce-templates ${dflags})
|
set(dflags -linkonce-templates ${dflags})
|
||||||
endif()
|
endif()
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
|
|
@ -136,8 +136,16 @@ but is guaranteed to follow it.
|
||||||
version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
|
version(Windows) wchar[] toWStringz(const(char)[] narrow, ref SmallBuffer!wchar buffer) nothrow
|
||||||
{
|
{
|
||||||
import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar;
|
import core.sys.windows.winnls : CP_ACP, MultiByteToWideChar;
|
||||||
|
|
||||||
|
version (IN_LLVM)
|
||||||
|
{
|
||||||
|
import dmd.root.filename : CodePage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// assume filenames encoded in system default Windows ANSI code page
|
// assume filenames encoded in system default Windows ANSI code page
|
||||||
enum CodePage = CP_ACP;
|
enum CodePage = CP_ACP;
|
||||||
|
}
|
||||||
|
|
||||||
if (narrow is null)
|
if (narrow is null)
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -44,7 +44,7 @@ version (Windows)
|
||||||
|
|
||||||
version (IN_LLVM)
|
version (IN_LLVM)
|
||||||
{
|
{
|
||||||
private enum CodePage = CP_UTF8;
|
enum CodePage = CP_UTF8;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -84,6 +84,19 @@ llvm::StringRef getXRayInstructionThresholdString() {
|
||||||
return thresholdString;
|
return thresholdString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cl::opt<CFProtectionType> fCFProtection(
|
||||||
|
"fcf-protection",
|
||||||
|
cl::desc("Instrument control-flow architecture protection"), cl::ZeroOrMore,
|
||||||
|
cl::ValueOptional,
|
||||||
|
cl::values(clEnumValN(CFProtectionType::None, "none", ""),
|
||||||
|
clEnumValN(CFProtectionType::Branch, "branch", ""),
|
||||||
|
clEnumValN(CFProtectionType::Return, "return", ""),
|
||||||
|
clEnumValN(CFProtectionType::Full, "full", ""),
|
||||||
|
clEnumValN(CFProtectionType::Full, "",
|
||||||
|
"") // default to "full" if no argument specified
|
||||||
|
),
|
||||||
|
cl::init(CFProtectionType::None));
|
||||||
|
|
||||||
void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) {
|
void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) {
|
||||||
if (ASTPGOInstrGenFile.getNumOccurrences() > 0) {
|
if (ASTPGOInstrGenFile.getNumOccurrences() > 0) {
|
||||||
pgoMode = PGO_ASTBasedInstr;
|
pgoMode = PGO_ASTBasedInstr;
|
||||||
|
@ -110,6 +123,14 @@ void initializeInstrumentationOptionsFromCmdline(const llvm::Triple &triple) {
|
||||||
|
|
||||||
if (dmdFunctionTrace)
|
if (dmdFunctionTrace)
|
||||||
global.params.trace = true;
|
global.params.trace = true;
|
||||||
|
|
||||||
|
// fcf-protection is only valid for X86
|
||||||
|
if (fCFProtection != CFProtectionType::None &&
|
||||||
|
!(triple.getArch() == llvm::Triple::x86 ||
|
||||||
|
triple.getArch() == llvm::Triple::x86_64)) {
|
||||||
|
error(Loc(), "option '--fcf-protection' cannot be specified on this target "
|
||||||
|
"architecture");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace opts
|
} // namespace opts
|
||||||
|
|
|
@ -29,6 +29,9 @@ extern cl::opt<bool> instrumentFunctions;
|
||||||
extern cl::opt<bool> fXRayInstrument;
|
extern cl::opt<bool> fXRayInstrument;
|
||||||
llvm::StringRef getXRayInstructionThresholdString();
|
llvm::StringRef getXRayInstructionThresholdString();
|
||||||
|
|
||||||
|
enum class CFProtectionType { None = 0, Branch = 1, Return = 2, Full = 3 };
|
||||||
|
extern cl::opt<CFProtectionType> fCFProtection;
|
||||||
|
|
||||||
/// This initializes the instrumentation options, and checks the validity of the
|
/// This initializes the instrumentation options, and checks the validity of the
|
||||||
/// commandline flags. targetTriple should be initialized before calling this.
|
/// commandline flags. targetTriple should be initialized before calling this.
|
||||||
/// It should be called only once.
|
/// It should be called only once.
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
//
|
//
|
||||||
// Implements functionality related to plugins (`-plugin=...`).
|
// Implements functionality related to plugins (`-plugin=...`).
|
||||||
//
|
//
|
||||||
|
// Note: plugins can be LLVM-plugins (to be registered with the pass manager)
|
||||||
|
// or dlang-plugins for semantic analysis.
|
||||||
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "driver/plugins.h"
|
#include "driver/plugins.h"
|
||||||
|
@ -17,6 +20,7 @@
|
||||||
|
|
||||||
#include "dmd/errors.h"
|
#include "dmd/errors.h"
|
||||||
#include "dmd/globals.h"
|
#include "dmd/globals.h"
|
||||||
|
#include "dmd/module.h"
|
||||||
#include "llvm/Passes/PassBuilder.h"
|
#include "llvm/Passes/PassBuilder.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/DynamicLibrary.h"
|
#include "llvm/Support/DynamicLibrary.h"
|
||||||
|
@ -36,63 +40,120 @@ cl::list<std::string> pluginFiles("plugin", cl::CommaSeparated,
|
||||||
cl::desc("Pass plugins to load."),
|
cl::desc("Pass plugins to load."),
|
||||||
cl::value_desc("dynamic_library.so,lib2.so"));
|
cl::value_desc("dynamic_library.so,lib2.so"));
|
||||||
|
|
||||||
|
struct SemaPlugin {
|
||||||
|
llvm::sys::DynamicLibrary library;
|
||||||
|
void (*runSemanticAnalysis)(Module *);
|
||||||
|
|
||||||
|
SemaPlugin(const llvm::sys::DynamicLibrary &library,
|
||||||
|
void (*runSemanticAnalysis)(Module *))
|
||||||
|
: library(library), runSemanticAnalysis(runSemanticAnalysis) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
llvm::SmallVector<SemaPlugin, 1> sema_plugins;
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
// Tries to load plugin as SemanticAnalysis. Returns true on 'success', i.e. no
|
||||||
|
// further attempts needed.
|
||||||
|
bool loadSemanticAnalysisPlugin(const std::string &filename) {
|
||||||
|
std::string errorString;
|
||||||
|
auto library = llvm::sys::DynamicLibrary::getPermanentLibrary(
|
||||||
|
filename.c_str(), &errorString);
|
||||||
|
if (!library.isValid()) {
|
||||||
|
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
|
||||||
|
errorString.c_str());
|
||||||
|
return true; // No success, but no need to try loading again as LLVM plugin.
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemanticAnalysis plugins need to export the `runSemanticAnalysis` function.
|
||||||
|
void *runSemanticAnalysisFnPtr =
|
||||||
|
library.getAddressOfSymbol("runSemanticAnalysis");
|
||||||
|
|
||||||
|
// If the symbol isn't found, this is probably an LLVM plugin.
|
||||||
|
if (!runSemanticAnalysisFnPtr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sema_plugins.emplace_back(
|
||||||
|
library, reinterpret_cast<void (*)(Module *)>(runSemanticAnalysisFnPtr));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads plugin for the legacy pass manager. The static constructor of
|
||||||
|
/// the plugin should take care of the plugins registering themself with the
|
||||||
|
/// rest of LDC/LLVM.
|
||||||
|
void loadLLVMPluginLegacyPM(const std::string &filename) {
|
||||||
|
std::string errorString;
|
||||||
|
if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(),
|
||||||
|
&errorString)) {
|
||||||
|
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
|
||||||
|
errorString.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if LDC_LLVM_VER >= 1400
|
#if LDC_LLVM_VER >= 1400
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
llvm::SmallVector<llvm::PassPlugin, 1> plugins;
|
llvm::SmallVector<llvm::PassPlugin, 1> llvm_plugins;
|
||||||
}
|
|
||||||
/// Loads all plugins for the new pass manager. These plugins will need to be
|
/// Loads plugin for the new pass manager. The plugin will need to be
|
||||||
/// added When building the optimization pipeline.
|
/// added explicitly when building the optimization pipeline.
|
||||||
void loadAllPluginsNewPM() {
|
void loadLLVMPluginNewPM(const std::string &filename) {
|
||||||
for (auto &filename : pluginFiles) {
|
|
||||||
auto plugin = llvm::PassPlugin::Load(filename);
|
auto plugin = llvm::PassPlugin::Load(filename);
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
|
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
|
||||||
llvm::toString(plugin.takeError()).c_str());
|
llvm::toString(plugin.takeError()).c_str());
|
||||||
continue;
|
return;
|
||||||
}
|
|
||||||
plugins.emplace_back(plugin.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) {
|
|
||||||
for (auto &plugin : plugins) {
|
|
||||||
plugin.registerPassBuilderCallbacks(PB);
|
|
||||||
}
|
}
|
||||||
|
llvm_plugins.emplace_back(plugin.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
#endif // LDC_LLVM_VER >= 1400
|
#endif // LDC_LLVM_VER >= 1400
|
||||||
|
|
||||||
/// Loads all plugins for the legacy pass manaager. The static constructor of
|
void loadLLVMPlugin(const std::string &filename) {
|
||||||
/// each plugin should take care of the plugins registering themself with the
|
#if LDC_LLVM_VER >= 1400
|
||||||
/// rest of LDC/LLVM.
|
if (opts::isUsingLegacyPassManager())
|
||||||
void loadAllPluginsLegacyPM() {
|
loadLLVMPluginLegacyPM(filename);
|
||||||
|
else
|
||||||
|
loadLLVMPluginNewPM(filename);
|
||||||
|
#else
|
||||||
|
loadLLVMPluginLegacyPM(filename);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAllPlugins() {
|
||||||
for (auto &filename : pluginFiles) {
|
for (auto &filename : pluginFiles) {
|
||||||
std::string errorString;
|
// First attempt to load plugin as SemanticAnalysis plugin. If unsuccesfull,
|
||||||
if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(filename.c_str(),
|
// load as LLVM plugin.
|
||||||
&errorString)) {
|
auto success = loadSemanticAnalysisPlugin(filename);
|
||||||
error(Loc(), "Error loading plugin '%s': %s", filename.c_str(),
|
if (!success)
|
||||||
errorString.c_str());
|
loadLLVMPlugin(filename);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void registerAllPluginsWithPassBuilder(llvm::PassBuilder &PB) {
|
||||||
#if LDC_LLVM_VER >= 1400
|
#if LDC_LLVM_VER >= 1400
|
||||||
void loadAllPlugins() {
|
for (auto &plugin : llvm_plugins) {
|
||||||
if (opts::isUsingLegacyPassManager())
|
plugin.registerPassBuilderCallbacks(PB);
|
||||||
loadAllPluginsLegacyPM();
|
}
|
||||||
else
|
|
||||||
loadAllPluginsNewPM();
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void loadAllPlugins() { loadAllPluginsLegacyPM(); }
|
|
||||||
void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void runAllSemanticAnalysisPlugins(Module *m) {
|
||||||
|
for (auto &plugin : sema_plugins) {
|
||||||
|
assert(plugin.runSemanticAnalysis);
|
||||||
|
plugin.runSemanticAnalysis(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else // #if LDC_ENABLE_PLUGINS
|
#else // #if LDC_ENABLE_PLUGINS
|
||||||
|
|
||||||
|
class Module;
|
||||||
|
|
||||||
void loadAllPlugins() {}
|
void loadAllPlugins() {}
|
||||||
void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {}
|
void registerAllPluginsWithPassBuilder(llvm::PassBuilder &) {}
|
||||||
|
void runAllSemanticAnalysisPlugins(Module *m) {}
|
||||||
|
|
||||||
#endif // LDC_ENABLE_PLUGINS
|
#endif // LDC_ENABLE_PLUGINS
|
||||||
|
|
|
@ -1188,7 +1188,7 @@ LLConstant *DtoConstExpInit(const Loc &loc, Type *targetType, Expression *exp) {
|
||||||
val = llvm::ConstantArray::get(at, elements);
|
val = llvm::ConstantArray::get(at, elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)numTotalVals;
|
(void)numTotalVals; (void) product; // Silence unused variable warning when assert is disabled.
|
||||||
assert(product == numTotalVals);
|
assert(product == numTotalVals);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,8 +393,27 @@ void registerModuleInfo(Module *m) {
|
||||||
emitModuleRefToSection(mangle, moduleInfoSym);
|
emitModuleRefToSection(mangle, moduleInfoSym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addModuleFlags(llvm::Module &m) {
|
||||||
|
#if LDC_LLVM_VER >= 1500
|
||||||
|
const auto ModuleMinFlag = llvm::Module::Min;
|
||||||
|
#else
|
||||||
|
const auto ModuleMinFlag = llvm::Module::Warning; // Fallback value
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (opts::fCFProtection == opts::CFProtectionType::Return ||
|
||||||
|
opts::fCFProtection == opts::CFProtectionType::Full) {
|
||||||
|
m.addModuleFlag(ModuleMinFlag, "cf-protection-return", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts::fCFProtection == opts::CFProtectionType::Branch ||
|
||||||
|
opts::fCFProtection == opts::CFProtectionType::Full) {
|
||||||
|
m.addModuleFlag(ModuleMinFlag, "cf-protection-branch", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
void codegenModule(IRState *irs, Module *m) {
|
void codegenModule(IRState *irs, Module *m) {
|
||||||
TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc);
|
TimeTraceScope timeScope("Generate IR", m->toChars(), m->loc);
|
||||||
|
|
||||||
|
@ -446,6 +465,8 @@ void codegenModule(IRState *irs, Module *m) {
|
||||||
addCoverageAnalysisInitializer(m);
|
addCoverageAnalysisInitializer(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addModuleFlags(irs->module);
|
||||||
|
|
||||||
gIR = nullptr;
|
gIR = nullptr;
|
||||||
irs->dmodule = nullptr;
|
irs->dmodule = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,20 @@ import dmd.dmodule;
|
||||||
|
|
||||||
extern(C++) void dcomputeSemanticAnalysis(Module m);
|
extern(C++) void dcomputeSemanticAnalysis(Module m);
|
||||||
extern(C) int hasComputeAttr(Dsymbol m);
|
extern(C) int hasComputeAttr(Dsymbol m);
|
||||||
|
extern(C++) void runAllSemanticAnalysisPlugins(Module m);
|
||||||
|
|
||||||
extern(C++) void extraLDCSpecificSemanticAnalysis(ref Modules modules)
|
extern(C++) void extraLDCSpecificSemanticAnalysis(ref Modules modules)
|
||||||
{
|
{
|
||||||
|
// First finish DCompute SemA for all modules, before calling plugins.
|
||||||
foreach(m; modules[])
|
foreach(m; modules[])
|
||||||
{
|
{
|
||||||
if (hasComputeAttr(m))
|
if (hasComputeAttr(m)) {
|
||||||
dcomputeSemanticAnalysis(m);
|
dcomputeSemanticAnalysis(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(m; modules[])
|
||||||
|
{
|
||||||
|
runAllSemanticAnalysisPlugins(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "dmd/mtype.h"
|
#include "dmd/mtype.h"
|
||||||
#include "dmd/target.h"
|
#include "dmd/target.h"
|
||||||
#include "driver/cl_options.h"
|
#include "driver/cl_options.h"
|
||||||
|
#include "driver/cl_options_instrumentation.h"
|
||||||
#include "driver/linker.h"
|
#include "driver/linker.h"
|
||||||
#include "gen/abi/abi.h"
|
#include "gen/abi/abi.h"
|
||||||
#include "gen/irstate.h"
|
#include "gen/irstate.h"
|
||||||
|
@ -309,6 +310,11 @@ Expression *Target::getTargetInfo(const char *name_, const Loc &loc) {
|
||||||
Loc(), static_cast<unsigned>(global.params.cplusplus), Type::tint32);
|
Loc(), static_cast<unsigned>(global.params.cplusplus), Type::tint32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name == "CET") {
|
||||||
|
auto cet = opts::fCFProtection.getValue();
|
||||||
|
return IntegerExp::create(loc, static_cast<unsigned>(cet), Type::tint32);
|
||||||
|
}
|
||||||
|
|
||||||
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX
|
#if LDC_LLVM_SUPPORTED_TARGET_SPIRV || LDC_LLVM_SUPPORTED_TARGET_NVPTX
|
||||||
if (name == "dcomputeTargets") {
|
if (name == "dcomputeTargets") {
|
||||||
Expressions* exps = createExpressions();
|
Expressions* exps = createExpressions();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
set( LDC2_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_EXE} )
|
set( LDC2_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_EXE} )
|
||||||
set( LDCPROFDATA_BIN ${PROJECT_BINARY_DIR}/bin/ldc-profdata )
|
set( LDCPROFDATA_BIN ${PROJECT_BINARY_DIR}/bin/ldc-profdata )
|
||||||
set( LDCPRUNECACHE_BIN ${PROJECT_BINARY_DIR}/bin/${LDCPRUNECACHE_EXE} )
|
set( LDCPRUNECACHE_BIN ${PROJECT_BINARY_DIR}/bin/${LDCPRUNECACHE_EXE} )
|
||||||
|
set( LDCBUILDPLUGIN_BIN ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE} )
|
||||||
set( TIMETRACE2TXT_BIN ${PROJECT_BINARY_DIR}/bin/${TIMETRACE2TXT_EXE} )
|
set( TIMETRACE2TXT_BIN ${PROJECT_BINARY_DIR}/bin/${TIMETRACE2TXT_EXE} )
|
||||||
set( LLVM_TOOLS_DIR ${LLVM_ROOT_DIR}/bin )
|
set( LLVM_TOOLS_DIR ${LLVM_ROOT_DIR}/bin )
|
||||||
set( LDC2_BIN_DIR ${PROJECT_BINARY_DIR}/bin )
|
set( LDC2_BIN_DIR ${PROJECT_BINARY_DIR}/bin )
|
||||||
|
|
31
tests/codegen/fcf_protection.d
Normal file
31
tests/codegen/fcf_protection.d
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// Test -fcf-protection
|
||||||
|
|
||||||
|
// REQUIRES: target_X86
|
||||||
|
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t.ll %s -d-version=NOTHING && FileCheck %s --check-prefix=NOTHING < %t.ll
|
||||||
|
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_branch.ll %s --fcf-protection=branch -d-version=BRANCH && FileCheck %s --check-prefix=BRANCH < %t_branch.ll
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_return.ll %s --fcf-protection=return -d-version=RETURN && FileCheck %s --check-prefix=RETURN < %t_return.ll
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_full.ll %s --fcf-protection=full -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_full.ll
|
||||||
|
// RUN: %ldc -mtriple=x86_64-linux-gnu -output-ll -of=%t_noarg.ll %s --fcf-protection -d-version=FULL && FileCheck %s --check-prefix=FULL < %t_noarg.ll
|
||||||
|
|
||||||
|
// NOTHING-NOT: cf-prot
|
||||||
|
// BRANCH-DAG: "cf-protection-branch", i32 1
|
||||||
|
// RETURN-DAG: "cf-protection-return", i32 1
|
||||||
|
// FULL-DAG: "cf-protection-branch", i32 1
|
||||||
|
// FULL-DAG: "cf-protection-return", i32 1
|
||||||
|
|
||||||
|
void foo() {}
|
||||||
|
|
||||||
|
version(NOTHING) {
|
||||||
|
static assert(__traits(getTargetInfo, "CET") == 0);
|
||||||
|
}
|
||||||
|
version(BRANCH) {
|
||||||
|
static assert(__traits(getTargetInfo, "CET") == 1);
|
||||||
|
}
|
||||||
|
version(RETURN) {
|
||||||
|
static assert(__traits(getTargetInfo, "CET") == 2);
|
||||||
|
}
|
||||||
|
version(FULL) {
|
||||||
|
static assert(__traits(getTargetInfo, "CET") == 3);
|
||||||
|
}
|
|
@ -17,10 +17,12 @@ OFF = False
|
||||||
config.ldc2_bin = "@LDC2_BIN@"
|
config.ldc2_bin = "@LDC2_BIN@"
|
||||||
config.ldcprofdata_bin = "@LDCPROFDATA_BIN@"
|
config.ldcprofdata_bin = "@LDCPROFDATA_BIN@"
|
||||||
config.ldcprunecache_bin = "@LDCPRUNECACHE_BIN@"
|
config.ldcprunecache_bin = "@LDCPRUNECACHE_BIN@"
|
||||||
|
config.ldcbuildplugin_bin = "@LDCBUILDPLUGIN_BIN@"
|
||||||
config.timetrace2txt_bin = "@TIMETRACE2TXT_BIN@"
|
config.timetrace2txt_bin = "@TIMETRACE2TXT_BIN@"
|
||||||
config.ldc2_bin_dir = "@LDC2_BIN_DIR@"
|
config.ldc2_bin_dir = "@LDC2_BIN_DIR@"
|
||||||
config.ldc2_lib_dir = "@LDC2_LIB_DIR@"
|
config.ldc2_lib_dir = "@LDC2_LIB_DIR@"
|
||||||
config.ldc2_runtime_dir = "@RUNTIME_DIR@"
|
config.ldc2_runtime_dir = "@RUNTIME_DIR@"
|
||||||
|
config.ldc2_source_dir = "@PROJECT_SOURCE_DIR@"
|
||||||
config.test_source_root = "@TESTS_IR_DIR@"
|
config.test_source_root = "@TESTS_IR_DIR@"
|
||||||
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
|
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
|
||||||
config.llvm_version = @LDC_LLVM_VER@
|
config.llvm_version = @LDC_LLVM_VER@
|
||||||
|
@ -156,6 +158,7 @@ config.substitutions.append( ('%ldc', config.ldc2_bin) )
|
||||||
config.substitutions.append( ('%gnu_make', config.gnu_make_bin) )
|
config.substitutions.append( ('%gnu_make', config.gnu_make_bin) )
|
||||||
config.substitutions.append( ('%profdata', config.ldcprofdata_bin) )
|
config.substitutions.append( ('%profdata', config.ldcprofdata_bin) )
|
||||||
config.substitutions.append( ('%prunecache', config.ldcprunecache_bin) )
|
config.substitutions.append( ('%prunecache', config.ldcprunecache_bin) )
|
||||||
|
config.substitutions.append( ('%buildplugin', config.ldcbuildplugin_bin + " --ldcSrcDir=" + config.ldc2_source_dir ) )
|
||||||
config.substitutions.append( ('%timetrace2txt', config.timetrace2txt_bin) )
|
config.substitutions.append( ('%timetrace2txt', config.timetrace2txt_bin) )
|
||||||
config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, 'llvm-spirv')) )
|
config.substitutions.append( ('%llvm-spirv', os.path.join(config.llvm_tools_dir, 'llvm-spirv')) )
|
||||||
config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) )
|
config.substitutions.append( ('%llc', os.path.join(config.llvm_tools_dir, 'llc')) )
|
||||||
|
|
26
tests/plugins/basic_sema_plugin.d
Normal file
26
tests/plugins/basic_sema_plugin.d
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// REQUIRES: Plugins
|
||||||
|
|
||||||
|
// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86 (but not for all CI testers...)
|
||||||
|
// UNSUPPORTED: Darwin && host_X86
|
||||||
|
|
||||||
|
// RUN: split-file %s %t --leading-lines
|
||||||
|
// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build
|
||||||
|
// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d
|
||||||
|
|
||||||
|
//--- plugin.d
|
||||||
|
import dmd.dmodule : Module;
|
||||||
|
import dmd.errors;
|
||||||
|
import dmd.location;
|
||||||
|
|
||||||
|
extern(C) void runSemanticAnalysis(Module m) {
|
||||||
|
if (m.md) {
|
||||||
|
warning(m.md.loc, "It works!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- testcase.d
|
||||||
|
// CHECK: testcase.d([[@LINE+1]]): Warning: It works!
|
||||||
|
module testcase;
|
||||||
|
int testfunction(int i) {
|
||||||
|
return i * 2;
|
||||||
|
}
|
|
@ -1,8 +1,28 @@
|
||||||
|
import lit.formats
|
||||||
|
import lit.util
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import platform
|
import platform
|
||||||
|
import string
|
||||||
import re
|
import re
|
||||||
|
import subprocess
|
||||||
|
import glob
|
||||||
|
|
||||||
if (config.plugins_supported):
|
if (config.plugins_supported):
|
||||||
config.available_features.add('Plugins')
|
config.available_features.add('Plugins')
|
||||||
config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config')
|
config.environment['LLVM_CONFIG'] = os.path.join(config.llvm_tools_dir, 'llvm-config')
|
||||||
config.environment['LLVM_VERSION'] = str(config.llvm_version)
|
config.environment['LLVM_VERSION'] = str(config.llvm_version)
|
||||||
|
|
||||||
|
# Set feature that tells us that the just-built LDC is ABI compatible with the host D compiler
|
||||||
|
# For our tets, the required ABI compatibility seems OK since at least LDC 1.30.
|
||||||
|
# If the compiler is built not by LDC but another compiler, then assume the ABI to be incompatible.
|
||||||
|
command = [config.ldc2_bin, '--version']
|
||||||
|
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
|
||||||
|
text1 = p.stdout.readline() # Ex.: "LDC - the LLVM D compiler (1.33.0-git-716f627)"
|
||||||
|
text2 = p.stdout.readline() # Ex.: " based on DMD v2.103.1 and LLVM 14.0.0"
|
||||||
|
text3 = p.stdout.readline() # Ex.: " built with LDC - the LLVM D compiler (1.33.0-beta2)"
|
||||||
|
host_version = re.compile(' built with LDC.* \(1\.([0-9]+).*\)').match(text3)
|
||||||
|
if (host_version and int(host_version.group(1)) >= 30): # 30 = LDC 1.30
|
||||||
|
config.available_features.add('ABI_compatible_with_host_D')
|
||||||
|
|
||||||
|
|
||||||
|
|
61
tests/plugins/visitor_example.d
Normal file
61
tests/plugins/visitor_example.d
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// REQUIRES: Plugins
|
||||||
|
// REQUIRES: ABI_compatible_with_host_D
|
||||||
|
|
||||||
|
// For some reason this test fails with missing symbol linking issues (or crash) with macOS on Intel x86.
|
||||||
|
// UNSUPPORTED: Darwin && host_X86
|
||||||
|
|
||||||
|
// RUN: split-file %s %t --leading-lines
|
||||||
|
// RUN: %buildplugin %t/plugin.d -of=%t/plugin%so --buildDir=%t/build
|
||||||
|
// RUN: %ldc -wi -c -o- --plugin=%t/plugin%so %t/testcase.d 2>&1 | FileCheck %t/testcase.d
|
||||||
|
|
||||||
|
//--- plugin.d
|
||||||
|
import dmd.dmodule;
|
||||||
|
import dmd.errors;
|
||||||
|
import dmd.location;
|
||||||
|
import dmd.visitor;
|
||||||
|
import dmd.declaration;
|
||||||
|
import dmd.dsymbol;
|
||||||
|
|
||||||
|
extern(C++) class MyVisitor : SemanticTimeTransitiveVisitor {
|
||||||
|
alias visit = SemanticTimeTransitiveVisitor.visit;
|
||||||
|
|
||||||
|
override void visit(VarDeclaration vd) {
|
||||||
|
if (vd.aliasTuple) {
|
||||||
|
vd.aliasTuple.foreachVar((s) {
|
||||||
|
auto vardecl = s.isVarDeclaration();
|
||||||
|
if (vardecl && vardecl.type.needsDestruction()) {
|
||||||
|
warning(vardecl.loc, "It works!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern(C) void runSemanticAnalysis(Module m) {
|
||||||
|
scope v = new MyVisitor();
|
||||||
|
if (!m.members)
|
||||||
|
return;
|
||||||
|
m.members.foreachDsymbol((s) {
|
||||||
|
s.accept(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//--- testcase.d
|
||||||
|
alias AliasSeq(TList...) = TList;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct A {
|
||||||
|
~this() {
|
||||||
|
i *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
{
|
||||||
|
// CHECK: testcase.d([[@LINE+1]]): Warning:
|
||||||
|
AliasSeq!(A, A) params;
|
||||||
|
i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i == 4);
|
||||||
|
}
|
|
@ -60,3 +60,25 @@ build_d_executable(
|
||||||
${COMPILE_D_MODULES_SEPARATELY}
|
${COMPILE_D_MODULES_SEPARATELY}
|
||||||
)
|
)
|
||||||
install(PROGRAMS ${TIMETRACE2TXT_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
install(PROGRAMS ${TIMETRACE2TXT_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
# Only build ldc-build-plugin tool for platforms where plugins are actually enabled.
|
||||||
|
if(LDC_ENABLE_PLUGINS)
|
||||||
|
configure_file(${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in ${PROJECT_BINARY_DIR}/ldc-build-plugin.d @ONLY)
|
||||||
|
set(LDC_BUILD_PLUGIN_EXE ldc-build-plugin)
|
||||||
|
set(LDC_BUILD_PLUGIN_EXE ${LDC_BUILD_PLUGIN_EXE} PARENT_SCOPE) # needed for correctly populating lit.site.cfg.in
|
||||||
|
set(LDC_BUILD_PLUGIN_EXE_NAME ${PROGRAM_PREFIX}${LDC_BUILD_PLUGIN_EXE}${PROGRAM_SUFFIX})
|
||||||
|
set(LDC_BUILD_PLUGIN_EXE_FULL ${PROJECT_BINARY_DIR}/bin/${LDC_BUILD_PLUGIN_EXE_NAME}${CMAKE_EXECUTABLE_SUFFIX})
|
||||||
|
build_d_executable(
|
||||||
|
"${LDC_BUILD_PLUGIN_EXE}"
|
||||||
|
"${LDC_BUILD_PLUGIN_EXE_FULL}"
|
||||||
|
"${PROJECT_BINARY_DIR}/ldc-build-plugin.d"
|
||||||
|
"${DFLAGS_BUILD_TYPE}"
|
||||||
|
""
|
||||||
|
"${PROJECT_SOURCE_DIR}/tools/ldc-build-plugin.d.in"
|
||||||
|
""
|
||||||
|
${COMPILE_D_MODULES_SEPARATELY}
|
||||||
|
)
|
||||||
|
install(PROGRAMS ${LDC_BUILD_PLUGIN_EXE_FULL} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
239
tools/ldc-build-plugin.d.in
Normal file
239
tools/ldc-build-plugin.d.in
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
module ldcBuildRuntime;
|
||||||
|
|
||||||
|
import core.stdc.stdlib : exit;
|
||||||
|
import std.algorithm;
|
||||||
|
import std.array;
|
||||||
|
import std.file;
|
||||||
|
import std.path;
|
||||||
|
import std.stdio;
|
||||||
|
|
||||||
|
version (OSX)
|
||||||
|
version = Darwin;
|
||||||
|
else version (iOS)
|
||||||
|
version = Darwin;
|
||||||
|
else version (TVOS)
|
||||||
|
version = Darwin;
|
||||||
|
else version (WatchOS)
|
||||||
|
version = Darwin;
|
||||||
|
|
||||||
|
struct Config {
|
||||||
|
string ldcExecutable;
|
||||||
|
string buildDir;
|
||||||
|
string ldcSourceDir;
|
||||||
|
string[] dFlags;
|
||||||
|
string[] linkerFlags;
|
||||||
|
bool verbose;
|
||||||
|
string[] ldcArgs;
|
||||||
|
string userWorkDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
version (Windows) enum exeSuffix = ".exe";
|
||||||
|
else enum exeSuffix = "";
|
||||||
|
|
||||||
|
string defaultLdcExecutable;
|
||||||
|
Config config;
|
||||||
|
|
||||||
|
int main(string[] args) {
|
||||||
|
enum exeName = "ldc2" ~ exeSuffix;
|
||||||
|
defaultLdcExecutable = buildPath(thisExePath.dirName, exeName);
|
||||||
|
config.userWorkDir = getcwd();
|
||||||
|
|
||||||
|
parseCommandLine(args);
|
||||||
|
|
||||||
|
findLdcExecutable();
|
||||||
|
|
||||||
|
prepareBuildDir();
|
||||||
|
|
||||||
|
prepareLdcSource();
|
||||||
|
|
||||||
|
build();
|
||||||
|
|
||||||
|
if (config.verbose)
|
||||||
|
writefln(".: Plugin library built successfully.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void findLdcExecutable() {
|
||||||
|
if (config.ldcExecutable !is null) {
|
||||||
|
if (!config.ldcExecutable.exists) {
|
||||||
|
writefln(".: Error: LDC executable not found: %s", config.ldcExecutable);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
config.ldcExecutable = config.ldcExecutable.absolutePath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultLdcExecutable.exists) {
|
||||||
|
config.ldcExecutable = defaultLdcExecutable;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writefln(".: Please specify LDC executable via '--ldc=<path/to/ldc2%s>'. Aborting.", exeSuffix);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareBuildDir() {
|
||||||
|
if (config.buildDir is null)
|
||||||
|
config.buildDir = "ldc-build-plugin.tmp";
|
||||||
|
|
||||||
|
if (!config.buildDir.exists) {
|
||||||
|
if (config.verbose)
|
||||||
|
writefln(".: Creating build directory: %s", config.buildDir);
|
||||||
|
mkdirRecurse(config.buildDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.buildDir = config.buildDir.absolutePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareLdcSource() {
|
||||||
|
if (config.ldcSourceDir !is null) {
|
||||||
|
if (!config.ldcSourceDir.exists) {
|
||||||
|
writefln(".: Error: LDC source directory not found: %s", config.ldcSourceDir);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
config.ldcSourceDir = config.ldcSourceDir.absolutePath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ldcSrc = "ldc-src";
|
||||||
|
config.ldcSourceDir = buildPath(config.buildDir, ldcSrc);
|
||||||
|
if (buildPath(config.ldcSourceDir, "dmd").exists)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Download & extract LDC source archive if <buildDir>/ldc-src/dmd doesn't exist yet.
|
||||||
|
|
||||||
|
const wd = WorkingDirScope(config.buildDir);
|
||||||
|
|
||||||
|
auto ldcVersion = "@LDC_VERSION@";
|
||||||
|
void removeVersionSuffix(string beginning) {
|
||||||
|
const suffixIndex = ldcVersion.countUntil(beginning);
|
||||||
|
if (suffixIndex > 0)
|
||||||
|
ldcVersion = ldcVersion[0 .. suffixIndex];
|
||||||
|
}
|
||||||
|
removeVersionSuffix("git-");
|
||||||
|
removeVersionSuffix("-dirty");
|
||||||
|
|
||||||
|
import std.format : format;
|
||||||
|
const localArchiveFile = "ldc-src.zip";
|
||||||
|
if (!localArchiveFile.exists) {
|
||||||
|
const url = "https://github.com/ldc-developers/ldc/releases/download/v%1$s/ldc-%1$s-src.zip".format(ldcVersion);
|
||||||
|
writefln(".: Downloading LDC source archive: %s", url);
|
||||||
|
import std.net.curl : download;
|
||||||
|
download(url, localArchiveFile);
|
||||||
|
if (getSize(localArchiveFile) < 1_048_576) {
|
||||||
|
writefln(".: Error: downloaded file is corrupt; has LDC v%s been released?", ldcVersion);
|
||||||
|
writefln(" You can work around this by manually downloading a src package and moving it to: %s",
|
||||||
|
buildPath(config.buildDir, localArchiveFile));
|
||||||
|
localArchiveFile.remove;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extractZipArchive(localArchiveFile, ".");
|
||||||
|
rename("ldc-%1$s-src".format(ldcVersion), ldcSrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void build() {
|
||||||
|
string[] args = [
|
||||||
|
config.ldcExecutable,
|
||||||
|
"-I" ~ config.ldcSourceDir,
|
||||||
|
"--d-version=IN_LLVM",
|
||||||
|
"-J" ~ buildPath(config.ldcSourceDir, "dmd", "res"),
|
||||||
|
"--shared",
|
||||||
|
"--defaultlib=",
|
||||||
|
"--od=" ~ config.buildDir
|
||||||
|
];
|
||||||
|
|
||||||
|
version (Darwin) {
|
||||||
|
args ~= "-L-Wl,-undefined,dynamic_lookup";
|
||||||
|
}
|
||||||
|
|
||||||
|
args ~= config.ldcArgs;
|
||||||
|
|
||||||
|
exec(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** helpers ***/
|
||||||
|
|
||||||
|
struct WorkingDirScope {
|
||||||
|
string originalPath;
|
||||||
|
this(string path) { originalPath = getcwd(); chdir(path); }
|
||||||
|
~this() { chdir(originalPath); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void exec(string[] command) {
|
||||||
|
import std.process;
|
||||||
|
|
||||||
|
static string quoteIfNeeded(string arg) {
|
||||||
|
const r = arg.findAmong(" ;");
|
||||||
|
return !r.length ? arg : "'" ~ arg ~ "'";
|
||||||
|
}
|
||||||
|
string flattened = command.map!quoteIfNeeded.join(" ");
|
||||||
|
if (config.verbose) {
|
||||||
|
writefln(".: Invoking: %s", flattened);
|
||||||
|
stdout.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pid = spawnProcess(command, null, std.process.Config.none, config.userWorkDir);
|
||||||
|
const exitStatus = wait(pid);
|
||||||
|
|
||||||
|
if (exitStatus != 0) {
|
||||||
|
if (config.verbose)
|
||||||
|
writeln(".: Error: command failed with status ", exitStatus);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void extractZipArchive(string archivePath, string destination) {
|
||||||
|
import std.zip;
|
||||||
|
|
||||||
|
auto archive = new ZipArchive(std.file.read(archivePath));
|
||||||
|
foreach (name, am; archive.directory) {
|
||||||
|
const destPath = buildNormalizedPath(destination, name);
|
||||||
|
|
||||||
|
const isDir = name.endsWith("/");
|
||||||
|
const destDir = isDir ? destPath : destPath.dirName;
|
||||||
|
mkdirRecurse(destDir);
|
||||||
|
|
||||||
|
if (!isDir)
|
||||||
|
std.file.write(destPath, archive.expand(am));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseCommandLine(string[] args) {
|
||||||
|
import std.getopt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
arraySep = ";";
|
||||||
|
auto helpInformation = getopt(
|
||||||
|
args,
|
||||||
|
std.getopt.config.passThrough,
|
||||||
|
"ldc", "Path to LDC executable (default: '" ~ defaultLdcExecutable ~ "')", &config.ldcExecutable,
|
||||||
|
"buildDir", "Path to build directory (default: './ldc-build-plugin.tmp')", &config.buildDir,
|
||||||
|
"ldcSrcDir", "Path to LDC source directory (if not specified: downloads & extracts source archive into '<buildDir>/ldc-src')", &config.ldcSourceDir,
|
||||||
|
"dFlags", "Extra LDC flags for the D module (separated by ';')", &config.dFlags,
|
||||||
|
"verbose|v", "Verbose output (e.g. showing the compile commandline)", &config.verbose,
|
||||||
|
"linkerFlags", "Extra C linker flags for shared libraries and testrunner executables (separated by ';')", &config.linkerFlags
|
||||||
|
);
|
||||||
|
|
||||||
|
// getopt() has removed all consumed args from `args`
|
||||||
|
// Remaining arguments are interpreted as LDC arguments (e.g. plugin source files and -of=<output file>).
|
||||||
|
config.ldcArgs = args[1 .. $];
|
||||||
|
|
||||||
|
if (helpInformation.helpWanted) {
|
||||||
|
defaultGetoptPrinter(
|
||||||
|
"OVERVIEW: Builds a Semantic Analysis plugin for LDC.\n\n" ~
|
||||||
|
"USAGE: ldc-build-plugin [options] sourcefiles... -of=<output file>\n\n" ~
|
||||||
|
"OPTIONS:\n" ~
|
||||||
|
" Unrecognized options are passed through to LDC.",
|
||||||
|
helpInformation.options
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
writefln("Error processing command line arguments: %s", e.msg);
|
||||||
|
writeln("Use '--help' for help.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue