From: Hans Wennborg Date: Tue, 10 Sep 2013 20:18:04 +0000 (+0000) Subject: clang-cl: Support building DLLs (PR17083) X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6d0a8d56b4386bd3f1091bf15d88491ad1299913;p=clang clang-cl: Support building DLLs (PR17083) This adds driver support for building DLLs (the /LD and /LDd flags). It basically does two things: runtime selection and passing -dll and -implib to the linker. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@190428 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Driver/CLCompatOptions.td b/include/clang/Driver/CLCompatOptions.td index 574830c07f..44418d2775 100644 --- a/include/clang/Driver/CLCompatOptions.td +++ b/include/clang/Driver/CLCompatOptions.td @@ -99,6 +99,8 @@ def _SLASH_Fe : CLJoined<"Fe">, def _SLASH_Fo : CLJoined<"Fo">, HelpText<"Set output object file, or directory (ends in / or \\)">, MetaVarName<"">; +def _SLASH_LD : CLFlag<"LD">, HelpText<"Create DLL">; +def _SLASH_LDd : CLFlag<"LDd">, HelpText<"Create debug DLL">; def _SLASH_link : CLRemainingArgs<"link">, HelpText<"Forward options to the linker">, MetaVarName<"">; def _SLASH_MD : CLFlag<"MD">, diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 1f2915ae32..9bbbaae33e 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1585,9 +1585,11 @@ void Driver::BuildJobsForAction(Compilation &C, static const char *MakeCLOutputFilename(const ArgList &Args, StringRef ArgValue, StringRef BaseName, types::ID FileType) { SmallString<128> Filename = ArgValue; - assert(!ArgValue.empty() && "Output filename argument must not be empty."); - if (llvm::sys::path::is_separator(Filename.back())) { + if (ArgValue.empty()) { + // If the argument is empty, output to BaseName in the current dir. + Filename = BaseName; + } else if (llvm::sys::path::is_separator(Filename.back())) { // If the argument is a directory, output to BaseName in that dir. llvm::sys::path::append(Filename, BaseName); } @@ -1595,6 +1597,13 @@ static const char *MakeCLOutputFilename(const ArgList &Args, StringRef ArgValue, if (!llvm::sys::path::has_extension(ArgValue)) { // If the argument didn't provide an extension, then set it. const char *Extension = types::getTypeTempSuffix(FileType, true); + + if (FileType == types::TY_Image && + Args.hasArg(options::OPT__SLASH_LD, options::OPT__SLASH_LDd)) { + // The output file is a dll. + Extension = "dll"; + } + llvm::sys::path::replace_extension(Filename, Extension); } @@ -1656,12 +1665,11 @@ const char *Driver::GetNamedOutputPath(Compilation &C, StringRef Val = C.getArgs().getLastArg(options::OPT__SLASH_Fe)->getValue(); NamedOutput = MakeCLOutputFilename(C.getArgs(), Val, BaseName, types::TY_Image); - } else if (JA.getType() == types::TY_Image) { + } else if (JA.getType() == types::TY_Image) { if (IsCLMode()) { // clang-cl uses BaseName for the executable name. - SmallString<128> Filename = BaseName; - llvm::sys::path::replace_extension(Filename, "exe"); - NamedOutput = C.getArgs().MakeArgString(Filename.c_str()); + NamedOutput = MakeCLOutputFilename(C.getArgs(), "", BaseName, + types::TY_Image); } else if (MultipleArchs && BoundArch) { SmallString<128> Output(DefaultImageName.c_str()); Output += "-"; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index d43758e3ad..79cdb042a9 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3714,6 +3714,11 @@ ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args, void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs) const { unsigned RTOptionID = options::OPT__SLASH_MT; + if (Args.hasArg(options::OPT__SLASH_LDd)) + // The /LDd option implies /MTd. The dependent lib part can be overridden, + // but defining _DEBUG is sticky. + RTOptionID = options::OPT__SLASH_MTd; + if (Arg *A = Args.getLastArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd, options::OPT__SLASH_MT, @@ -3723,6 +3728,8 @@ void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs) const { switch(RTOptionID) { case options::OPT__SLASH_MD: + if (Args.hasArg(options::OPT__SLASH_LDd)) + CmdArgs.push_back("-D_DEBUG"); CmdArgs.push_back("-D_MT"); CmdArgs.push_back("-D_DLL"); CmdArgs.push_back("--dependent-lib=msvcrt"); @@ -3734,6 +3741,8 @@ void Clang::AddClangCLArgs(const ArgList &Args, ArgStringList &CmdArgs) const { CmdArgs.push_back("--dependent-lib=msvcrtd"); break; case options::OPT__SLASH_MT: + if (Args.hasArg(options::OPT__SLASH_LDd)) + CmdArgs.push_back("-D_DEBUG"); CmdArgs.push_back("-D_MT"); CmdArgs.push_back("--dependent-lib=libcmt"); break; @@ -6584,13 +6593,29 @@ void visualstudio::Link::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-nologo"); + bool DLL = Args.hasArg(options::OPT__SLASH_LD, options::OPT__SLASH_LDd); + + if (DLL) { + CmdArgs.push_back(Args.MakeArgString("-dll")); + + SmallString<128> ImplibName(Output.getFilename()); + llvm::sys::path::replace_extension(ImplibName, "lib"); + CmdArgs.push_back(Args.MakeArgString(std::string("-implib:") + + ImplibName.str())); + } + if (getToolChain().getDriver().getOrParseSanitizerArgs(Args).needsAsanRt()) { CmdArgs.push_back(Args.MakeArgString("-debug")); CmdArgs.push_back(Args.MakeArgString("-incremental:no")); SmallString<128> LibSanitizer(getToolChain().getDriver().ResourceDir); - // FIXME: Handle 64-bit. Use asan_dll_thunk.dll when building a DLL. - llvm::sys::path::append( - LibSanitizer, "lib", "windows", "clang_rt.asan-i386.lib"); + llvm::sys::path::append(LibSanitizer, "lib", "windows"); + if (DLL) { + // FIXME: Not sure what the final name of the thunk lib is. + llvm::sys::path::append(LibSanitizer, "clang_rt.asan-i386-dll_thunk.lib"); + } else { + llvm::sys::path::append(LibSanitizer, "clang_rt.asan-i386.lib"); + } + // FIXME: Handle 64-bit. CmdArgs.push_back(Args.MakeArgString(LibSanitizer)); } diff --git a/test/Driver/cl-link.c b/test/Driver/cl-link.c index f6e18b8f1a..f46298a194 100644 --- a/test/Driver/cl-link.c +++ b/test/Driver/cl-link.c @@ -17,3 +17,17 @@ // ASAN: "-incremental:no" // ASAN: "{{.*}}clang_rt.asan-i386.lib" // ASAN: "{{.*}}cl-link{{.*}}.obj" + +// RUN: %clang_cl /LD -### %s 2>&1 | FileCheck --check-prefix=DLL %s +// RUN: %clang_cl /LDd -### %s 2>&1 | FileCheck --check-prefix=DLL %s +// DLL: link.exe +// "-dll" + +// RUN: %clang_cl /LD /Tc%s -### -fsanitize=address 2>&1 | FileCheck --check-prefix=ASAN-DLL %s +// RUN: %clang_cl /LDd /Tc%s -### -fsanitize=address 2>&1 | FileCheck --check-prefix=ASAN-DLL %s +// ASAN-DLL: link.exe +// ASAN-DLL: "-dll" +// ASAN-DLL: "-debug" +// ASAN-DLL: "-incremental:no" +// ASAN-DLL: "{{.*}}clang_rt.asan-i386-dll_thunk.lib" +// ASAN-DLL: "{{.*}}cl-link{{.*}}.obj" diff --git a/test/Driver/cl-outputs.c b/test/Driver/cl-outputs.c index 4a8fc5ca49..636ef96f60 100644 --- a/test/Driver/cl-outputs.c +++ b/test/Driver/cl-outputs.c @@ -39,24 +39,54 @@ // RUN: %clang_cl -### -- %s 2>&1 | FileCheck -check-prefix=DEFAULTEXE %s // DEFAULTEXE: cl-outputs.exe +// RUN: %clang_cl /LD -### -- %s 2>&1 | FileCheck -check-prefix=DEFAULTDLL %s +// RUN: %clang_cl /LDd -### -- %s 2>&1 | FileCheck -check-prefix=DEFAULTDLL %s +// DEFAULTDLL: "-out:cl-outputs.dll" +// DEFAULTDLL: "-implib:cl-outputs.lib" + // RUN: %clang_cl /Fefoo -### -- %s 2>&1 | FileCheck -check-prefix=FeNOEXT %s -// FeNOEXT: foo.exe +// FeNOEXT: "-out:foo.exe" + +// RUN: %clang_cl /Fefoo /LD -### -- %s 2>&1 | FileCheck -check-prefix=FeNOEXTDLL %s +// RUN: %clang_cl /Fefoo /LDd -### -- %s 2>&1 | FileCheck -check-prefix=FeNOEXTDLL %s +// FeNOEXTDLL: "-out:foo.dll" +// FeNOEXTDLL: "-implib:foo.lib" // RUN: %clang_cl /Fefoo.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeEXT %s -// FeEXT: foo.ext +// FeEXT: "-out:foo.ext" + +// RUN: %clang_cl /LD /Fefoo.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeEXTDLL %s +// RUN: %clang_cl /LDd /Fefoo.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeEXTDLL %s +// FeEXTDLL: "-out:foo.ext" +// FeEXTDLL: "-implib:foo.lib" // RUN: %clang_cl /Fefoo.dir/ -### -- %s 2>&1 | FileCheck -check-prefix=FeDIR %s -// FeDIR: foo.dir{{[/\\]+}}cl-outputs.exe +// FeDIR: "-out:foo.dir{{[/\\]+}}cl-outputs.exe" + +// RUN: %clang_cl /LD /Fefoo.dir/ -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRDLL %s +// RUN: %clang_cl /LDd /Fefoo.dir/ -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRDLL %s +// FeDIRDLL: "-out:foo.dir{{[/\\]+}}cl-outputs.dll" +// FeDIRDLL: "-implib:foo.dir{{[/\\]+}}cl-outputs.lib" // RUN: %clang_cl /Fefoo.dir/a -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAME %s -// FeDIRNAME: foo.dir{{[/\\]+}}a.exe +// FeDIRNAME: "-out:foo.dir{{[/\\]+}}a.exe" + +// RUN: %clang_cl /LD /Fefoo.dir/a -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEDLL %s +// RUN: %clang_cl /LDd /Fefoo.dir/a -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEDLL %s +// FeDIRNAMEDLL: "-out:foo.dir{{[/\\]+}}a.dll" +// FeDIRNAMEDLL: "-implib:foo.dir{{[/\\]+}}a.lib" // RUN: %clang_cl /Fefoo.dir/a.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEEXT %s -// FeDIRNAMEEXT: foo.dir{{[/\\]+}}a.ext +// FeDIRNAMEEXT: "-out:foo.dir{{[/\\]+}}a.ext" + +// RUN: %clang_cl /LD /Fefoo.dir/a.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEEXTDLL %s +// RUN: %clang_cl /LDd /Fefoo.dir/a.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEEXTDLL %s +// FeDIRNAMEEXTDLL: "-out:foo.dir{{[/\\]+}}a.ext" +// FeDIRNAMEEXTDLL: "-implib:foo.dir{{[/\\]+}}a.lib" // RUN: %clang_cl /Fe -### 2>&1 | FileCheck -check-prefix=FeMISSINGARG %s // FeMISSINGARG: error: argument to '/Fe' is missing (expected 1 value) // RUN: %clang_cl /Fefoo /Febar -### -- %s 2>&1 | FileCheck -check-prefix=FeOVERRIDE %s // FeOVERRIDE: warning: overriding '/Fefoo' option with '/Febar' -// FeOVERRIDE: bar.exe +// FeOVERRIDE: "-out:bar.exe" diff --git a/test/Driver/cl-runtime-flags.c b/test/Driver/cl-runtime-flags.c index 520093c302..0f550dbe6d 100644 --- a/test/Driver/cl-runtime-flags.c +++ b/test/Driver/cl-runtime-flags.c @@ -20,6 +20,7 @@ // CHECK-MT: "--dependent-lib=oldnames" // RUN: %clang_cl -### /MTd -- %s 2>&1 | FileCheck -check-prefix=CHECK-MTd %s +// RUN: %clang_cl -### /LD /MTd -- %s 2>&1 | FileCheck -check-prefix=CHECK-MTd %s // CHECK-MTd: "-D_DEBUG" // CHECK-MTd: "-D_MT" // CHECK-MTd-NOT: "-D_DLL" @@ -39,3 +40,47 @@ // CHECK-MDd: "-D_DLL" // CHECK-MDd: "--dependent-lib=msvcrtd" // CHECK-MDd: "--dependent-lib=oldnames" + +// RUN: %clang_cl -### /LD -- %s 2>&1 | FileCheck -check-prefix=CHECK-LD %s +// RUN: %clang_cl -### /LD /MT -- %s 2>&1 | FileCheck -check-prefix=CHECK-LD %s +// CHECK-LD-NOT: "-D_DEBUG" +// CHECK-LD: "-D_MT" +// CHECK-LD-NOT: "-D_DLL" +// CHECK-LD: "--dependent-lib=libcmt" + +// RUN: %clang_cl -### /LDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDd %s +// RUN: %clang_cl -### /LDd /MTd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDd %s +// CHECK-LDd: "-D_DEBUG" +// CHECK-LDd: "-D_MT" +// CHECK-LDd-NOT: "-D_DLL" +// CHECK-LDd: "--dependent-lib=libcmtd" + +// RUN: %clang_cl -### /LDd /MT -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDdMT %s +// RUN: %clang_cl -### /MT /LDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDdMT %s +// CHECK-LDdMT: "-D_DEBUG" +// CHECK-LDdMT: "-D_MT" +// CHECK-LDdMT-NOT: "-D_DLL" +// CHECK-LDdMT: "--dependent-lib=libcmt" + +// RUN: %clang_cl -### /LD /MD -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMD %s +// RUN: %clang_cl -### /MD /LD -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMD %s +// CHECK-LDMD-NOT: "-D_DEBUG" +// CHECK-LDMD: "-D_MT" +// CHECK-LDMD: "-D_DLL" +// CHECK-LDMD: "--dependent-lib=msvcrt" + +// RUN: %clang_cl -### /LDd /MD -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDdMD %s +// RUN: %clang_cl -### /MD /LDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDdMD %s +// CHECK-LDdMD: "-D_DEBUG" +// CHECK-LDdMD: "-D_MT" +// CHECK-LDdMD: "-D_DLL" +// CHECK-LDdMD: "--dependent-lib=msvcrt" + +// RUN: %clang_cl -### /LD /MDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMDd %s +// RUN: %clang_cl -### /MDd /LD -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMDd %s +// RUN: %clang_cl -### /LDd /MDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMDd %s +// RUN: %clang_cl -### /MDd /LDd -- %s 2>&1 | FileCheck -check-prefix=CHECK-LDMDd %s +// CHECK-LDMDd: "-D_DEBUG" +// CHECK-LDMDd: "-D_MT" +// CHECK-LDMDd: "-D_DLL" +// CHECK-LDMDd: "--dependent-lib=msvcrtd"