Skip to content

Commit afb00fe

Browse files
committed
Add CMAKE_GNUtoMS option to convert GNU .dll.a to MS .lib
Teach the Windows-GNU.cmake platform file to look for Visual Studio tools matching the target ABI. Add an extra step to the link command for shared libraries and executables that export symbols and on which a new GNUtoMS property is set (initialized by the CMAKE_GNUtoMS option). Tell the GNU linker to output a module definition (.def) file listing exported symbols in addition to the GNU-format import library (.dll.a). Pass the .def file to the MS "lib" tool to construct a MS-format DLL import library (.lib). Teach the install(TARGETS) command to install the MS import library next to the GNU one. Teach the install(EXPORT) and export() command to set the IMPORTED_IMPLIB property pointing at the import library to use the import library matching the tools in the importing project.
1 parent 61e8629 commit afb00fe

15 files changed

+176
-2
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ configure crlf=input
88
*.sh.in crlf=input
99

1010
*.bat -crlf
11+
*.bat.in -crlf
1112
*.dsp -crlf
1213
*.dsptemplate -crlf
1314
*.dsw -crlf

Modules/Platform/GNUtoMS_lib.bat.in

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@echo off
2+
call "@CMAKE_GNUtoMS_BAT@"
3+
lib /machine:"@CMAKE_GNUtoMS_ARCH@" %*

Modules/Platform/GNUtoMS_lib.cmake

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Usage: cmake -Dlib=lib.bat -Ddef=out.def -Ddll=out.dll -Dimp=out.dll.a -P GNUtoMS_lib.cmake
2+
get_filename_component(name ${dll} NAME) # .dll file name
3+
string(REGEX REPLACE "\\.dll\\.a$" ".lib" out "${imp}") # .dll.a -> .lib
4+
execute_process(
5+
COMMAND ${lib} /def:${def} /name:${name} /out:${out}
6+
RESULT_VARIABLE res
7+
)
8+
if(res)
9+
message(FATAL_ERROR "lib failed: ${res}")
10+
endif()
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__windows_compiler_gnu_abi(C)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__windows_compiler_gnu_abi(CXX)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__windows_compiler_gnu_abi(Fortran)

Modules/Platform/Windows-GNU.cmake

+54
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ macro(__windows_compiler_gnu lang)
108108
set(CMAKE_${lang}_LINK_EXECUTABLE
109109
"<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Wl,--out-implib,<TARGET_IMPLIB> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>")
110110

111+
list(APPEND CMAKE_${lang}_ABI_FILES "Platform/Windows-GNU-${lang}-ABI")
112+
111113
# Support very long lists of object files.
112114
if("${CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG}" STREQUAL "@")
113115
foreach(rule CREATE_SHARED_MODULE CREATE_SHARED_LIBRARY LINK_EXECUTABLE)
@@ -125,3 +127,55 @@ macro(__windows_compiler_gnu lang)
125127
endforeach()
126128
endif()
127129
endmacro()
130+
131+
macro(__windows_compiler_gnu_abi lang)
132+
if(CMAKE_NO_GNUtoMS)
133+
set(CMAKE_GNUtoMS 0)
134+
else()
135+
option(CMAKE_GNUtoMS "Convert GNU import libraries to MS format (requires Visual Studio)" OFF)
136+
endif()
137+
138+
if(CMAKE_GNUtoMS AND NOT CMAKE_GNUtoMS_LIB)
139+
# Find MS development environment setup script for this architecture.
140+
if("${CMAKE_SIZEOF_VOID_P}" EQUAL 4)
141+
find_program(CMAKE_GNUtoMS_VCVARS NAMES vcvars32.bat
142+
DOC "Visual Studio vcvars32.bat"
143+
PATHS
144+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0\\Setup\\VC;ProductDir]/bin"
145+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0\\Setup\\VC;ProductDir]/bin"
146+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0\\Setup\\VC;ProductDir]/bin"
147+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0\\Setup\\VC;ProductDir]/bin"
148+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1\\Setup\\VC;ProductDir]/bin"
149+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\6.0\\Setup\\Microsoft Visual C++;ProductDir]/bin"
150+
)
151+
set(CMAKE_GNUtoMS_ARCH x86)
152+
elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL 8)
153+
find_program(CMAKE_GNUtoMS_VCVARS NAMES vcvarsamd64.bat
154+
DOC "Visual Studio vcvarsamd64.bat"
155+
PATHS
156+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0\\Setup\\VC;ProductDir]/bin/amd64"
157+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0\\Setup\\VC;ProductDir]/bin/amd64"
158+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0\\Setup\\VC;ProductDir]/bin/amd64"
159+
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0\\Setup\\VC;ProductDir]/bin/amd64"
160+
)
161+
set(CMAKE_GNUtoMS_ARCH amd64)
162+
endif()
163+
set_property(CACHE CMAKE_GNUtoMS_VCVARS PROPERTY ADVANCED 1)
164+
if(CMAKE_GNUtoMS_VCVARS)
165+
# Create helper script to run lib.exe from MS environment.
166+
string(REPLACE "/" "\\" CMAKE_GNUtoMS_BAT "${CMAKE_GNUtoMS_VCVARS}")
167+
set(CMAKE_GNUtoMS_LIB ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeGNUtoMS_lib.bat)
168+
configure_file(${CMAKE_ROOT}/Modules/Platform/GNUtoMS_lib.bat.in ${CMAKE_GNUtoMS_LIB})
169+
else()
170+
message(WARNING "Disabling CMAKE_GNUtoMS option because CMAKE_GNUtoMS_VCVARS is not set.")
171+
set(CMAKE_GNUtoMS 0)
172+
endif()
173+
endif()
174+
175+
if(CMAKE_GNUtoMS)
176+
# Teach CMake how to create a MS import library at link time.
177+
set(CMAKE_${lang}_GNUtoMS_RULE " -Wl,--output-def,<TARGET_NAME>.def"
178+
"<CMAKE_COMMAND> -Dlib=\"${CMAKE_GNUtoMS_LIB}\" -Ddef=\"<TARGET_NAME>.def\" -Ddll=\"<TARGET>\" -Dimp=\"<TARGET_IMPLIB>\" -P \"${CMAKE_ROOT}/Modules/Platform/GNUtoMS_lib.cmake\""
179+
)
180+
endif()
181+
endmacro()

Source/cmDocumentVariables.cxx

+9
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,15 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
11111111
false,
11121112
"Variables that Control the Build");
11131113

1114+
cm->DefineProperty
1115+
("CMAKE_GNUtoMS", cmProperty::VARIABLE,
1116+
"Convert GNU import libraries (.dll.a) to MS format (.lib).",
1117+
"This variable is used to initialize the GNUtoMS property on targets "
1118+
"when they are created. "
1119+
"See that target property for additional information.",
1120+
false,
1121+
"Variables that Control the Build");
1122+
11141123
cm->DefineProperty
11151124
("CMAKE_DEBUG_POSTFIX", cmProperty::VARIABLE,
11161125
"See variable CMAKE_<CONFIG>_POSTFIX.",

Source/cmExportBuildFileGenerator.cxx

+2
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ ::SetImportLocationProperty(const char* config, std::string const& suffix,
125125
std::string prop = "IMPORTED_IMPLIB";
126126
prop += suffix;
127127
std::string value = target->GetFullPath(config, true);
128+
target->GetImplibGNUtoMS(value, value,
129+
"${CMAKE_IMPORT_LIBRARY_SUFFIX}");
128130
properties[prop] = value;
129131
}
130132
}

Source/cmInstallTargetGenerator.cxx

+24-2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
101101
std::string to1 = toDir + targetNameImport;
102102
filesFrom.push_back(from1);
103103
filesTo.push_back(to1);
104+
std::string targetNameImportLib;
105+
if(this->Target->GetImplibGNUtoMS(targetNameImport,
106+
targetNameImportLib))
107+
{
108+
filesFrom.push_back(fromDirConfig + targetNameImportLib);
109+
filesTo.push_back(toDir + targetNameImportLib);
110+
}
104111

105112
// An import library looks like a static library.
106113
type = cmTarget::STATIC_LIBRARY;
@@ -157,6 +164,13 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os,
157164
std::string to1 = toDir + targetNameImport;
158165
filesFrom.push_back(from1);
159166
filesTo.push_back(to1);
167+
std::string targetNameImportLib;
168+
if(this->Target->GetImplibGNUtoMS(targetNameImport,
169+
targetNameImportLib))
170+
{
171+
filesFrom.push_back(fromDirConfig + targetNameImportLib);
172+
filesTo.push_back(toDir + targetNameImportLib);
173+
}
160174

161175
// An import library looks like a static library.
162176
type = cmTarget::STATIC_LIBRARY;
@@ -314,7 +328,11 @@ std::string cmInstallTargetGenerator::GetInstallFilename(cmTarget* target,
314328
if(nameType == NameImplib)
315329
{
316330
// Use the import library name.
317-
fname = targetNameImport;
331+
if(!target->GetImplibGNUtoMS(targetNameImport, fname,
332+
"${CMAKE_IMPORT_LIBRARY_SUFFIX}"))
333+
{
334+
fname = targetNameImport;
335+
}
318336
}
319337
else if(nameType == NameReal)
320338
{
@@ -339,7 +357,11 @@ std::string cmInstallTargetGenerator::GetInstallFilename(cmTarget* target,
339357
if(nameType == NameImplib)
340358
{
341359
// Use the import library name.
342-
fname = targetNameImport;
360+
if(!target->GetImplibGNUtoMS(targetNameImport, fname,
361+
"${CMAKE_IMPORT_LIBRARY_SUFFIX}"))
362+
{
363+
fname = targetNameImport;
364+
}
343365
}
344366
else if(nameType == NameSO)
345367
{

Source/cmMakefileExecutableTargetGenerator.cxx

+7
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
241241
exeCleanFiles.push_back(this->Convert(targetFullPathImport.c_str(),
242242
cmLocalGenerator::START_OUTPUT,
243243
cmLocalGenerator::UNCHANGED));
244+
std::string implib;
245+
if(this->Target->GetImplibGNUtoMS(targetFullPathImport, implib))
246+
{
247+
exeCleanFiles.push_back(this->Convert(implib.c_str(),
248+
cmLocalGenerator::START_OUTPUT,
249+
cmLocalGenerator::UNCHANGED));
250+
}
244251
}
245252

246253
// List the PDB for cleaning only when the whole target is

Source/cmMakefileLibraryTargetGenerator.cxx

+7
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules
512512
libCleanFiles.push_back(this->Convert(targetFullPathImport.c_str(),
513513
cmLocalGenerator::START_OUTPUT,
514514
cmLocalGenerator::UNCHANGED));
515+
std::string implib;
516+
if(this->Target->GetImplibGNUtoMS(targetFullPathImport, implib))
517+
{
518+
libCleanFiles.push_back(this->Convert(implib.c_str(),
519+
cmLocalGenerator::START_OUTPUT,
520+
cmLocalGenerator::UNCHANGED));
521+
}
515522
}
516523

517524
// List the PDB for cleaning only when the whole target is

Source/cmMakefileTargetGenerator.cxx

+10
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,16 @@ ::AppendLinkDepends(std::vector<std::string>& depends)
16371637
std::string cmMakefileTargetGenerator::GetLinkRule(const char* linkRuleVar)
16381638
{
16391639
std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar);
1640+
if(this->Target->HasImplibGNUtoMS())
1641+
{
1642+
std::string ruleVar = "CMAKE_";
1643+
ruleVar += this->Target->GetLinkerLanguage(this->ConfigName);
1644+
ruleVar += "_GNUtoMS_RULE";
1645+
if(const char* rule = this->Makefile->GetDefinition(ruleVar.c_str()))
1646+
{
1647+
linkRule += rule;
1648+
}
1649+
}
16401650
return linkRule;
16411651
}
16421652

Source/cmTarget.cxx

+38
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,23 @@ void cmTarget::DefineProperties(cmake *cm)
979979
"If the variable CMAKE_Fortran_MODULE_DIRECTORY is set when a target "
980980
"is created its value is used to initialize this property.");
981981

982+
cm->DefineProperty
983+
("GNUtoMS", cmProperty::TARGET,
984+
"Convert GNU import library (.dll.a) to MS format (.lib).",
985+
"When linking a shared library or executable that exports symbols "
986+
"using GNU tools on Windows (MinGW/MSYS) with Visual Studio installed "
987+
"convert the import library (.dll.a) from GNU to MS format (.lib). "
988+
"Both import libraries will be installed by install(TARGETS) and "
989+
"exported by install(EXPORT) and export() to be linked by applications "
990+
"with either GNU- or MS-compatible tools."
991+
"\n"
992+
"If the variable CMAKE_GNUtoMS is set when a target "
993+
"is created its value is used to initialize this property. "
994+
"The variable must be set prior to the first command that enables "
995+
"a language such as project() or enable_language(). "
996+
"CMake provides the variable as an option to the user automatically "
997+
"when configuring on Windows with GNU tools.");
998+
982999
cm->DefineProperty
9831000
("XCODE_ATTRIBUTE_<an-attribute>", cmProperty::TARGET,
9841001
"Set Xcode target attributes directly.",
@@ -1195,6 +1212,7 @@ void cmTarget::SetMakefile(cmMakefile* mf)
11951212
this->SetPropertyDefault("RUNTIME_OUTPUT_DIRECTORY", 0);
11961213
this->SetPropertyDefault("Fortran_FORMAT", 0);
11971214
this->SetPropertyDefault("Fortran_MODULE_DIRECTORY", 0);
1215+
this->SetPropertyDefault("GNUtoMS", 0);
11981216
this->SetPropertyDefault("OSX_ARCHITECTURES", 0);
11991217
this->SetPropertyDefault("AUTOMOC", 0);
12001218
this->SetPropertyDefault("AUTOMOC_MOC_OPTIONS", 0);
@@ -3456,6 +3474,26 @@ void cmTarget::GetExecutableNames(std::string& name,
34563474
pdbName = prefix+base+".pdb";
34573475
}
34583476

3477+
//----------------------------------------------------------------------------
3478+
bool cmTarget::HasImplibGNUtoMS()
3479+
{
3480+
return this->HasImportLibrary() && this->GetPropertyAsBool("GNUtoMS");
3481+
}
3482+
3483+
//----------------------------------------------------------------------------
3484+
bool cmTarget::GetImplibGNUtoMS(std::string const& gnuName,
3485+
std::string& out, const char* newExt)
3486+
{
3487+
if(this->HasImplibGNUtoMS() &&
3488+
gnuName.size() > 6 && gnuName.substr(gnuName.size()-6) == ".dll.a")
3489+
{
3490+
out = gnuName.substr(0, gnuName.size()-6);
3491+
out += newExt? newExt : ".lib";
3492+
return true;
3493+
}
3494+
return false;
3495+
}
3496+
34593497
//----------------------------------------------------------------------------
34603498
void cmTarget::GenerateTargetManifest(const char* config)
34613499
{

Source/cmTarget.h

+8
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,14 @@ class cmTarget
369369
std::string& impName,
370370
std::string& pdbName, const char* config);
371371

372+
/** Does this target have a GNU implib to convert to MS format? */
373+
bool HasImplibGNUtoMS();
374+
375+
/** Convert the given GNU import library name (.dll.a) to a name with a new
376+
extension (.lib or ${CMAKE_IMPORT_LIBRARY_SUFFIX}). */
377+
bool GetImplibGNUtoMS(std::string const& gnuName, std::string& out,
378+
const char* newExt = 0);
379+
372380
/** Add the target output files to the global generator manifest. */
373381
void GenerateTargetManifest(const char* config);
374382

0 commit comments

Comments
 (0)