Skip to content

Commit

Permalink
Merge pull request #289 from rainers/build-mingw
Browse files Browse the repository at this point in the history
Build mingw COFF libraries
  • Loading branch information
MartinNowak committed Feb 17, 2018
2 parents 3e8e46a + 8d1b57e commit bd47972
Show file tree
Hide file tree
Showing 9 changed files with 952 additions and 21 deletions.
24 changes: 3 additions & 21 deletions .travis.yml
@@ -1,21 +1,3 @@
language: c

addons:
apt:
# https://github.com/SimonKagstrom/kcov/blob/master/INSTALL.md#user-content-ubuntu
packages: [shellcheck, binutils-dev, libcurl4-openssl-dev, zlib1g-dev, libdw-dev, libiberty-dev]

matrix:
include:
- os: linux
install:
- KCOV_VERSION=34
- curl -fsSL https://github.com/SimonKagstrom/kcov/archive/v"$KCOV_VERSION".tar.gz | tar -C "$HOME" -zxf -
- pushd "$HOME/kcov-$KCOV_VERSION"
- cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .
- make install -j$(nproc)
- popd
script: kcov --exclude-pattern=travis.sh $PWD/coverage ./travis.sh
after_success: bash <(curl -s https://codecov.io/bash) -s $PWD/coverage
- os: osx
script: ./travis.sh
branches:
except:
- /^build-/
16 changes: 16 additions & 0 deletions appveyor.yml
@@ -0,0 +1,16 @@
os: Visual Studio 2017

environment:
matrix:
- ARCH: x86
MINGW_VER: 5.0.2
D_VERSION: 2.077.1

artifacts:
- path: mingw-libs-$(MINGW_VER).zip
name: mingwlibs

build_script:
- call windows\build_mingw.bat

test_script: true
46 changes: 46 additions & 0 deletions windows/build_mingw.bat
@@ -0,0 +1,46 @@
@setlocal

set ROOT=%CD%

set DMD_URL=http://downloads.dlang.org/releases/2.x/%D_VERSION%/dmd.%D_VERSION%.windows.7z
echo DMD_URL=%DMD_URL%
appveyor DownloadFile %DMD_URL% -FileName dmd2.7z || exit /B 1
7z x dmd2.7z || exit /B 1
set PATH=%ROOT%\dmd2\windows\bin;%PATH%

echo b80b0c9d0158f9125e482b50fe00b70dde11d7a015ee687ca455fe2ea2ec8733 *w32api.src.tar.xz> sha256sums
echo 77233333f5440287840d134804bcecf3144ec3efc7fd7f7c6dce318e4e7146ee *mingwrt.src.tar.xz>> sha256sums

set MINGW_BASEURL=https://sourceforge.net/projects/mingw/files/MinGW/Base/
set W32API_URL=%MINGW_BASEURL%/w32api/w32api-%MINGW_VER%/w32api-%MINGW_VER%-mingw32-src.tar.xz
set MINGWRT_URL=%MINGW_BASEURL%/mingwrt/mingwrt-%MINGW_VER%/mingwrt-%MINGW_VER%-mingw32-src.tar.xz

appveyor DownloadFile %W32API_URL% -FileName w32api.src.tar.xz || exit /B 1
appveyor DownloadFile %MINGWRT_URL% -FileName mingwrt.src.tar.xz || exit /B 1

:: e.g. from git installation
sha256sum -c sha256sums || exit /B 1

7z x w32api.src.tar.xz || exit /B 1
7z x w32api.src.tar || exit /B 1

7z x mingwrt.src.tar.xz || exit /B 1
7z x mingwrt.src.tar || exit /B 1

move w32api-%MINGW_VER% w32api
move mingwrt-%MINGW_VER% mingwrt

cd windows\mingw
set w32api_lib=../../w32api/lib
set msvcrt_def_in=../../mingwrt/msvcrt-xref/msvcrt.def.in

call "c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
rem CWD might be changed by vcvars64.bat
cd %ROOT%\windows\mingw
dmd -run buildsdk.d x64 %w32api_lib% dmd2\windows\lib64 %msvcrt_def_in% || exit /B 1

call "c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat"
cd %ROOT%\windows\mingw
dmd -run buildsdk.d x86 %w32api_lib% dmd2\windows\lib32mscoff %msvcrt_def_in% || exit /B 1

7z a %ROOT%\mingw-libs-%MINGW_VER%.zip dmd2\windows
192 changes: 192 additions & 0 deletions windows/mingw/buildsdk.d
@@ -0,0 +1,192 @@
//
// Convert MingGW definition files to COFF import librries
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// usage: buildsdk [x86|x64] [def-folder] [output-folder] [msvcrt.def.in]
//
// Source files extracted from the MinGW reositories
//
// def-folder: https://sourceforge.net/p/mingw/mingw-org-wsl/ci/5.0-active/tree/w32api/lib/
// msvcrt.def.in: https://sourceforge.net/p/mingw/mingw-org-wsl/ci/5.0-active/tree/mingwrt/msvcrt-xref/msvcrt.def.in
//
// assumes VC tools cl,link,lib and ml installed and found through path
// and configured to the appropriate architecture
//

import std.file;
import std.regex;
import std.string;
import std.stdio;
import std.path;
import std.process;
import std.algorithm;

version = verbose;

void runShell(string cmd)
{
version(verbose)
writeln(cmd);
auto rc = executeShell(cmd);
if (rc.status)
{
writeln("'", cmd, "' failed with status ", rc.status);
writeln(rc.output);
throw new Exception("'" ~ cmd ~ "' failed");
}
}

// x86: the exported symbols have stdcall mangling (including trailing @n)
// but the internal names of the platform DLLs have names with @n stripped off
// lib /DEF doesn't support renaming symbols so we have to go through compiling
// a C file with the symbols and building a DLL with renamed exports to get
// the appropriate import library
//
// x64: strip any @ from the symbol names
bool def2implib(bool x64, string f, string dir, string linkopt = null)
{
static auto re = regex(r"@?([a-zA-Z0-9_]+)(@[0-9]*)");
char[] content = cast(char[])std.file.read(f);
auto pos = content.indexOf("EXPORTS");
if (pos < 0)
return false;

char[] def = content[0..pos];
char[] csrc;
bool[string] written;
auto lines = content[pos..$].splitLines;
foreach(line; lines)
{
line = line.strip;
if (line.length == 0 || line[0] == ';')
continue;
const(char)[] sym;
char[] cline;
auto m = matchFirst(line, re);
if (m)
{
if (x64)
def ~= m[1] ~ "\n";
else
def ~= m[0] ~ "=" ~ m[1] ~ "\n";
sym = m[1];
cline = "void " ~ m[1] ~ "() {}\n";
}
else
{
def ~= line ~ "\n";
if (line.endsWith(" DATA"))
{
sym = strip(line[0..$-5]);
cline = "int " ~ sym ~ ";\n";
}
else
{
auto idx = line.indexOf('=');
if (idx > 0)
sym = line[idx+1 .. $].strip;
else
sym = line;
cline = "void " ~ sym ~ "() {}\n";
}
}
if(sym.length && sym !in written)
{
csrc ~= cline;
written[sym.idup] = true;
}
}
string base = stripExtension(baseName(f));
string dirbase = dir ~ base;
std.file.write(dirbase ~ ".def", def);
std.file.write(dirbase ~ ".c", csrc);

runShell("cl /c /Fo" ~ dirbase ~ ".obj " ~ dirbase ~ ".c");
runShell("link /NOD /NOENTRY /DLL " ~ dirbase ~ ".obj /out:" ~ dirbase ~ ".dll /def:" ~ dirbase ~ ".def" ~ linkopt);

// cleanup
std.file.remove(dirbase ~ ".def");
std.file.remove(dirbase ~ ".c");
std.file.remove(dirbase ~ ".obj");
std.file.remove(dirbase ~ ".dll");
std.file.remove(dirbase ~ ".exp");
return true;
}

void buildLibs(bool x64, string defdir, string dir)
{
mkdirRecurse(dir);

//goto LnoDef;
foreach(f; std.file.dirEntries(defdir, SpanMode.shallow))
if (extension(f).toLower == ".def")
def2implib(x64, f, dir);
foreach(f; std.file.dirEntries(defdir ~ "/directx", SpanMode.shallow))
if (extension(f).toLower == ".def")
def2implib(x64, f, dir);

version(DDK) // disable for now
{
mkdirRecurse(dir ~ ddk);
foreach(f; std.file.dirEntries(defdir ~ "/ddk", SpanMode.shallow))
if (extension(f).toLower == ".def")
def2implib(x64, f, dir ~ "ddk/");
}
}

void buildMsvcrt(bool x64, string dir, string msvcdef)
{
string arch = x64 ? "x64" : "x86";
string lib = "lib /MACHINE:" ~ arch ~ " ";
string msvcrtlib = "msvcrt100.lib";

// build msvcrt.lib for VS2010
runShell("cl /EP -D__MSVCRT_VERSION__=0x10000000UL -D__DLLNAME__=msvcr100 " ~ msvcdef ~ " >" ~ dir ~ "msvcrt.def");
runShell(lib ~ "/OUT:" ~ dir ~ msvcrtlib ~ " /DEF:" ~ dir ~ "msvcrt.def"); // no translation necessary
runShell("cl /c /Zl /Fo" ~ dir ~ "msvcrt_stub0.obj /D_APPTYPE=0 msvcrt_stub.c");
runShell("cl /c /Zl /Fo" ~ dir ~ "msvcrt_stub1.obj /D_APPTYPE=1 msvcrt_stub.c");
runShell("cl /c /Zl /Fo" ~ dir ~ "msvcrt_stub2.obj /D_APPTYPE=2 msvcrt_stub.c");
runShell("cl /c /Zl /Fo" ~ dir ~ "msvcrt_data.obj msvcrt_data.c");
runShell("cl /c /Zl /Fo" ~ dir ~ "msvcrt_atexit.obj msvcrt_atexit.c");
auto files = ["msvcrt_stub0.obj", "msvcrt_stub1.obj", "msvcrt_stub2.obj", "msvcrt_data.obj", "msvcrt_atexit.obj" ];
if (!x64)
{
runShell("ml /c /Fo" ~ dir ~ "msvcrt_abs.obj msvcrt_abs.asm");
files ~= "msvcrt_abs.obj";
}
auto objs = files.map!(a => dir ~ a).join(" ");
runShell(lib ~ dir ~ msvcrtlib ~ " " ~ objs);

// create oldnames.lib (expected by dmd)
runShell("cl /c /Zl /Fo" ~ dir ~ "oldnames.obj oldnames.c");
runShell(lib ~ "/OUT:" ~ dir ~ "oldnames.lib " ~ dir ~ "oldnames.obj");

// create empty uuid.lib (expected by dmd, but UUIDs already in druntime)
std.file.write(dir ~ "empty.c", "");
runShell("cl /c /Zl /Fo" ~ dir ~ "uuid.obj " ~ dir ~ "empty.c");
runShell(lib ~ "/OUT:" ~ dir ~ "uuid.lib " ~ dir ~ "uuid.obj");

foreach(f; files)
std.file.remove(dir ~ f);
std.file.remove(dir ~ stripExtension(msvcrtlib) ~ ".exp");
std.file.remove(dir ~ "msvcrt.def");
std.file.remove(dir ~ "oldnames.obj");
std.file.remove(dir ~ "uuid.obj");
std.file.remove(dir ~ "empty.c");
}

void main(string[] args)
{
bool x64 = (args.length > 1 && args[1] == "x64");
string defdir = (args.length > 2 ? args[2] : "def");
string outdir = x64 ? "lib64/" : "lib32mscoff/";
if (args.length > 3)
outdir = args[3] ~ "/";
string msvcdef = (args.length > 4 ? args[4] : "msvcrt.def.in");

buildLibs(x64, defdir, outdir);
buildMsvcrt(x64, outdir, msvcdef);
}

0 comments on commit bd47972

Please sign in to comment.