diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58194a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*gcc*.tar.* +*MacOSX*.* +cpucount +build/ +target/ diff --git a/CHANGELOG b/CHANGELOG index f8976bb..5eae746 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +0.7: +- added: new compiler wrapper written in C++ +- added: '-oc-use-gcc-libs' option (uses './build_gcc.sh' libstdc++) +- added: 10.6 SDK support (10.4-10.9 are all supported now) +- added: 'sw_vers' tool, which is required by some projects +- changed: 'osxcross-conf', 'osxcross-env' and the (fake) 'dsymutil' are now implemented in the wrapper +- changed: switched to two-space indents + 0.6: - added: NetBSD support - added: dependencies installer script (Don Bright) @@ -16,7 +24,7 @@ - added: a workaround for buggy unistd.h headers 0.3: -- added: support 10.9 (Mavericks) SDK +- added: 10.9 SDK support - added: *-apple-darwin*-clang* symlinks (same as invoking o32-clang or o64-clang++) - changed: no need to build extra cctools binaries for targeting i386, symlinking the x86_64 builds works as well diff --git a/README.md b/README.md index a43c57c..e5da43a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ The goal of OSXCross is to provide a well working OS X cross toolchain for Linux ### HOW DOES IT WORK? ### -[Clang/LLVM is a cross compiler by default](http://clang.llvm.org/docs/CrossCompilation.html) and is now available on nearly every Linux distribution, +[Clang/LLVM is a cross compiler by default](http://clang.llvm.org/docs/CrossCompilation.html) +and is now available on nearly every Linux distribution, so we just need a proper [port](https://github.com/tpoechtrager/cctools-port) of the [cctools](http://www.opensource.apple.com/tarballs/cctools) (ld, lipo, ...) and the OS X SDK. @@ -90,6 +91,14 @@ You can use the shortcut `o32-...` or `i386-apple-darwin...` what ever you like ##### Building test.cpp with libc++: ##### +Note: libc++ requires Mac OS X 10.7 or newer! If you really need C++11 for +an older OS X version, then you can do the following: + +1. Build GCC so you have an up-to-date libstdc++ +2. Build your source code with GCC or with clang and '-oc-use-gcc-libs' + +Usage Examples: + * Clang: * C++98: `o32-clang++ -stdlib=libc++ test.cpp -o test` diff --git a/build.sh b/build.sh index 5e6ad43..294f21d 100755 --- a/build.sh +++ b/build.sh @@ -80,7 +80,7 @@ fi LINKER_VERSION=134.9 # Don't change this -OSXCROSS_VERSION=0.6 +OSXCROSS_VERSION=0.7 TARBALL_DIR=$BASE_DIR/tarballs BUILD_DIR=$BASE_DIR/build @@ -102,6 +102,8 @@ case $SDK_VERSION in *) echo "Invalid SDK Version" && exit 1 ;; esac +export TARGET + echo "" echo "Building OSXCross toolchain, Version: $OSXCROSS_VERSION" echo "" @@ -132,8 +134,6 @@ require autogen require automake require libtool -CLANG_TARGET_OPTION=`./oclang/check_target_option.sh` - pushd $BUILD_DIR &>/dev/null function remove_locks() @@ -287,108 +287,38 @@ set +e ln -s \ $SDK_DIR/MacOSX$SDK_VERSION.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/std*.h \ usr/include 2>/dev/null -$BASE_DIR/oclang/find_intrinsic_headers.sh $SDK_DIR/MacOSX$SDK_VERSION.sdk test ! -f "usr/include/float.h" && cp -f $BASE_DIR/oclang/quirks/float.h usr/include set -e popd &>/dev/null popd &>/dev/null -cp -f oclang/dsymutil $TARGET_DIR/bin - -WRAPPER=$TARGET_DIR/bin/x86_64-apple-$TARGET-oclang -cp -f oclang/oclang $WRAPPER - -WRAPPER_SCRIPT=`basename $WRAPPER` -WRAPPER_DIR=`dirname $WRAPPER` - -pushd $WRAPPER_DIR &>/dev/null - -ln -sf $WRAPPER_SCRIPT o32-clang -ln -sf $WRAPPER_SCRIPT o32-clang++ -ln -sf $WRAPPER_SCRIPT o32-clang++-libc++ - -ln -sf $WRAPPER_SCRIPT o64-clang -ln -sf $WRAPPER_SCRIPT o64-clang++ -ln -sf $WRAPPER_SCRIPT o64-clang++-libc++ - -ln -sf $WRAPPER_SCRIPT i386-apple-$TARGET-clang -ln -sf $WRAPPER_SCRIPT i386-apple-$TARGET-clang++ -ln -sf $WRAPPER_SCRIPT i386-apple-$TARGET-clang++-libc++ - -ln -sf $WRAPPER_SCRIPT x86_64-apple-$TARGET-clang -ln -sf $WRAPPER_SCRIPT x86_64-apple-$TARGET-clang++ -ln -sf $WRAPPER_SCRIPT x86_64-apple-$TARGET-clang++-libc++ - -popd &>/dev/null - OSXCROSS_CONF="$TARGET_DIR/bin/osxcross-conf" OSXCROSS_ENV="$TARGET_DIR/bin/osxcross-env" -rm -f $OSXCROSS_CONF $ENV_CONF +rm -f $OSXCROSS_CONF $OSXCROSS_ENV -echo "#!/usr/bin/env bash" > $OSXCROSS_CONF -echo "" >> $OSXCROSS_CONF -echo "pushd \"\${0%/*}\" &>/dev/null" >> $OSXCROSS_CONF -echo "" >> $OSXCROSS_CONF -echo "DIR=\`pwd\`" >> $OSXCROSS_CONF -echo "OSXCROSS_ROOT=\$DIR/../.." >> $OSXCROSS_CONF -echo "" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_VERSION=$OSXCROSS_VERSION\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_OSX_VERSION_MIN=$OSX_VERSION_MIN\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_TARGET=$TARGET\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_SDK_VERSION=$SDK_VERSION\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_SDK=\$DIR/../`basename $SDK_DIR`/MacOSX$SDK_VERSION.sdk\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_TARBALL_DIR=\$OSXCROSS_ROOT/`basename $TARBALL_DIR`\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_PATCH_DIR=\$OSXCROSS_ROOT/`basename $PATCH_DIR`\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_TARGET_DIR=\$OSXCROSS_ROOT/`basename $TARGET_DIR`\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_BUILD_DIR=\$OSXCROSS_ROOT/`basename $BUILD_DIR`\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_CCTOOLS_PATH=\$DIR\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_TARGET_OPTION=$CLANG_TARGET_OPTION\"" >> $OSXCROSS_CONF -echo "echo \"export OSXCROSS_LINKER_VERSION=$LINKER_VERSION\"" >> $OSXCROSS_CONF -echo "" >> $OSXCROSS_CONF -echo "popd &>/dev/null" >> $OSXCROSS_CONF -echo "" >> $OSXCROSS_CONF - -if [ -f $BUILD_DIR/cctools*/cctools/tmp/ldpath ]; then - LIB_PATH=:`cat $BUILD_DIR/cctools*/cctools/tmp/ldpath` -else - LIB_PATH="" -fi - -echo "#!/bin/sh" > $OSXCROSS_ENV -echo "" >> $OSXCROSS_ENV -echo "BDIR=\`readlink -f \\\`dirname \$0\\\`\`" >> $OSXCROSS_ENV -echo "" >> $OSXCROSS_ENV -echo "echo \"export PATH=\$PATH:\$BDIR\"" >> $OSXCROSS_ENV -echo "echo \"export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:\$BDIR/../lib${LIB_PATH}\"" >> $OSXCROSS_ENV - - -chmod +x $OSXCROSS_CONF $OSXCROSS_ENV +echo "compiling wrapper ..." +export OSX_VERSION_MIN +export LINKER_VERSION +$BASE_DIR/wrapper/build.sh 1>/dev/null export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:`cat $BUILD_DIR/cctools*/cctools/tmp/ldpath`" # libLTO.so echo "" if [ -n $OSX_VERSION_MIN ]; then -if [ `echo "${SDK_VERSION/u/}<$OSX_VERSION_MIN" | bc -l` -eq 1 ]; then - echo "OSX_VERSION_MIN must be <= SDK_VERSION" - trap "" EXIT - exit 1 -elif [ `echo "$OSX_VERSION_MIN<10.4" | bc -l` -eq 1 ]; then - echo "OSX_VERSION_MIN must be >= 10.4" - trap "" EXIT - exit 1 + if [ `echo "${SDK_VERSION/u/}<$OSX_VERSION_MIN" | bc -l` -eq 1 ]; then + echo "OSX_VERSION_MIN must be <= SDK_VERSION" + trap "" EXIT + exit 1 + elif [ `echo "$OSX_VERSION_MIN<10.4" | bc -l` -eq 1 ]; then + echo "OSX_VERSION_MIN must be >= 10.4" + trap "" EXIT + exit 1 + fi fi -if [ `echo "${SDK_VERSION/u/}>=10.9" | bc -l` -eq 1 ] && ( [ $OSX_VERSION_MIN == "default" ] || - [ `echo "$OSX_VERSION_MIN>=10.9" | bc -l` -eq 1 ] ); -then - export SCRIPT=`basename $0` - ./build_libcxx.sh || exit 0 -fi -fi # OSX_VERSION_MIN set - test_compiler o32-clang $BASE_DIR/oclang/test.c test_compiler o64-clang $BASE_DIR/oclang/test.c @@ -408,12 +338,33 @@ if [ `echo "${SDK_VERSION/u/}>=10.7" | bc -l` -eq 1 ]; then test_compiler_cxx11 o64-clang++ $BASE_DIR/oclang/test_libcxx.cpp fi +set +e +which csh &>/dev/null +HAVE_CSH=0 +[ $? -eq 0 ] && HAVE_CSH=1 + +if [ $HAVE_CSH -eq 0 ]; then + which tcsh &>/dev/null + [ $? -eq 0 ] && HAVE_CSH=1 +fi + +CSHRC="" +[ $HAVE_CSH -eq 1 ] && CSHRC=", ~/.cshrc" +set -e + echo "" echo "Now add" echo "" echo -e "\e[32m\`$OSXCROSS_ENV\`\e[0m" echo "" -echo "to your ~/.bashrc or ~/.profile (including the '\`')" +if [ $HAVE_CSH -eq 1 ]; then +echo "or in case of csh:" +echo "" +echo -e "\e[32msetenv PATH \`$OSXCROSS_ENV -v=PATH\`\e[0m" +echo -e "\e[32msetenv LD_LIBRARY_PATH \`$OSXCROSS_ENV -v=LD_LIBRARY_PATH\`\e[0m" +echo "" +fi +echo "to your ~/.bashrc${CSHRC} or ~/.profile (including the '\`')" echo "" echo "Done! Now you can use o32-clang(++) and o64-clang(++) like a normal compiler" diff --git a/build_gcc.sh b/build_gcc.sh index cd5c92c..3d56dc8 100755 --- a/build_gcc.sh +++ b/build_gcc.sh @@ -78,13 +78,7 @@ fi # have gcc popd &>/dev/null # build dir -WRAPPER=$OSXCROSS_TARGET_DIR/bin/x86_64-apple-${OSXCROSS_TARGET}-ogcc -cp ogcc/ogcc $WRAPPER - -WRAPPER_SCRIPT=`basename $WRAPPER` -WRAPPER_DIR=`dirname $WRAPPER` - -pushd $WRAPPER_DIR &>/dev/null +pushd $OSXCROSS_TARGET_DIR/bin &>/dev/null if [ ! -f i386-apple-$OSXCROSS_TARGET-base-gcc ]; then mv x86_64-apple-$OSXCROSS_TARGET-gcc x86_64-apple-$OSXCROSS_TARGET-base-gcc @@ -94,21 +88,14 @@ if [ ! -f i386-apple-$OSXCROSS_TARGET-base-gcc ]; then ln -sf x86_64-apple-$OSXCROSS_TARGET-base-g++ i386-apple-$OSXCROSS_TARGET-base-g++ fi -ln -sf $WRAPPER_SCRIPT o32-gcc -ln -sf $WRAPPER_SCRIPT o32-g++ -ln -sf $WRAPPER_SCRIPT o32-g++-libc++ +echo "compiling wrapper ..." -ln -sf $WRAPPER_SCRIPT o64-gcc -ln -sf $WRAPPER_SCRIPT o64-g++ -ln -sf $WRAPPER_SCRIPT o64-g++-libc++ +export TARGET=$OSXCROSS_TARGET +export OSX_VERSION_MIN=$OSXCROSS_OSX_VERSION_MIN +export LINKER_VERSION=$OSXCROSS_LINKER_VERSION -ln -sf $WRAPPER_SCRIPT i386-apple-$OSXCROSS_TARGET-gcc -ln -sf $WRAPPER_SCRIPT i386-apple-$OSXCROSS_TARGET-g++ -ln -sf $WRAPPER_SCRIPT i386-apple-$OSXCROSS_TARGET-g++-libc++ - -ln -sf $WRAPPER_SCRIPT x86_64-apple-$OSXCROSS_TARGET-gcc -ln -sf $WRAPPER_SCRIPT x86_64-apple-$OSXCROSS_TARGET-g++ -ln -sf $WRAPPER_SCRIPT x86_64-apple-$OSXCROSS_TARGET-g++-libc++ +TARGETCOMPILER=gcc \ + $BASE_DIR/wrapper/build.sh 1>/dev/null popd &>/dev/null # wrapper dir diff --git a/oclang/check_target_option.sh b/oclang/check_target_option.sh deleted file mode 100755 index ad77453..0000000 --- a/oclang/check_target_option.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -export LC_ALL="C" - -which clang 2>&1 1>/dev/null || exit 1 - -x=`clang -target i386-apple-darwin9 2>&1` - -case "$x" in - *i386-apple-darwin9*) - echo "-ccc-host-triple" - exit 0 - ;; -esac - -echo "-target" diff --git a/oclang/dsymutil b/oclang/dsymutil deleted file mode 100755 index d5c7a94..0000000 --- a/oclang/dsymutil +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# -# No-op script to bypass dsymutil invocation errors -# -# Please see: -# https://github.com/tpoechtrager/osxcross/issues/1 -# https://github.com/tpoechtrager/osxcross/pull/2 -# - -if [ $# -eq 0 ] || [ -n "$OSXCROSS_SHOW_DSYMUTIL_INVOCATION" ]; then - NAME_OF_PARENT_PROCESS=`basename \`ps -ocommand= -p $PPID 2>/dev/null | awk '{print $1}'\`` - - echo -en "\e[1mosxcross \e[35mwarning:\e[0m\e[1m dsymutil invocation is a no-op (dsymutil $@) " 1>&2 - - if [ -n "$NAME_OF_PARENT_PROCESS" ]; then - echo -e "(invoked by $NAME_OF_PARENT_PROCESS)\e[0m" 1>&2 - else - echo -e "\e[0m" 1>&2 - fi -fi diff --git a/oclang/find_intrinsic_headers.sh b/oclang/find_intrinsic_headers.sh deleted file mode 100755 index 85f3983..0000000 --- a/oclang/find_intrinsic_headers.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# help clang to find its own intrinsic headers -# this issue appears to be fixed in 3.4+ - -pushd "${0%/*}" &>/dev/null - -set -e - -SDK_DIR="$1" - -test -n "$SDK_DIR" || { echo "no SDK directory given" && exit 1; } -test -e "$SDK_DIR" || { echo "$SDK_DIR does not exist" && exit 1; } - -CLANG_VERSION=`echo "int main(){printf(\"%d.%d\",__clang_major__,__clang_minor__);return 0;}" | clang -xc -ansi -otest - 2>/dev/null && ./test && rm test` -CLANG_DIR=`dirname \`which clang\`` - -CLANG_INTRIN_DIR="$CLANG_DIR/../include/clang/$CLANG_VERSION/include" - -test ! -e "$CLANG_INTRIN_DIR" && CLANG_INTRIN_DIR="$CLANG_DIR/../lib/clang/$CLANG_VERSION/include" -test ! -e "$CLANG_INTRIN_DIR" && CLANG_INTRIN_DIR="$CLANG_DIR/../include/clang/$CLANG_VERSION/include" -test ! -e "$CLANG_INTRIN_DIR" && CLANG_INTRIN_DIR="$CLANG_DIR/../include/clang/$CLANG_VERSION" - -test -e "$CLANG_INTRIN_DIR" || { echo "cannot find clang intrinsics directory" && exit 1; } -test -f "$CLANG_INTRIN_DIR/xmmintrin.h" || { echo "xmmintrin.h does not exist in $CLANG_INTRIN_DIR" && exit 1; } - -echo "found clang intrinsic headers: $CLANG_INTRIN_DIR" - -test -f $CLANG_INTRIN_DIR/float.h && ln -sf $CLANG_INTRIN_DIR/float.h $SDK_DIR/usr/include -test -f $CLANG_INTRIN_DIR/stdarg.h && ln -sf $CLANG_INTRIN_DIR/stdarg.h $SDK_DIR/usr/include - -ln -sf $CLANG_INTRIN_DIR/*intrin*.h $SDK_DIR/usr/include -ln -sf $CLANG_INTRIN_DIR/mm*.h $SDK_DIR/usr/include -ln -sf $CLANG_INTRIN_DIR/*va*.h $SDK_DIR/usr/include -ln -sf $CLANG_INTRIN_DIR/*cpu*.h $SDK_DIR/usr/include -ln -sf $CLANG_INTRIN_DIR/*math*.h $SDK_DIR/usr/include -ln -sf $CLANG_INTRIN_DIR/*iso*.h $SDK_DIR/usr/include diff --git a/oclang/find_lto_header.sh b/oclang/find_lto_header.sh deleted file mode 100755 index b119629..0000000 --- a/oclang/find_lto_header.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash - -function try() -{ - LLVM_CONFIG="llvm-config$1" - which $LLVM_CONFIG &>/dev/null - - if [ $? -eq 0 ]; then - set -e - LLVM_INC_DIR=`$LLVM_CONFIG --includedir` - LLVM_LIB_DIR=`$LLVM_CONFIG --libdir` - ln -sf "$LLVM_INC_DIR/llvm-c/lto.h" "include/llvm-c/lto.h" - echo -n "export LDFLAGS+=\" -L$LLVM_LIB_DIR -lLTO \" " - echo -n "export CFLAGS+=\" -DLTO_SUPPORT=1 \" " - echo -n "export CXXFLAGS+=\" -DLTO_SUPPORT=1 \"" - exit 0 - fi -} - -try "" -try "-3.2" -try "-3.3" -try "-3.4" -try "-3.5" - -echo "echo \"can not find lto.h - make sure llvm-devel is installed on your system\"" -exit 1 diff --git a/oclang/oclang b/oclang/oclang deleted file mode 100755 index c88da59..0000000 --- a/oclang/oclang +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env bash - -pushd "${0%/*}" &>/dev/null -eval `./osxcross-env` -eval `./osxcross-conf` -popd &>/dev/null - -type=`basename $0` - -if [[ $type == *o64* ]] || [[ $type == *x86_64* ]]; then - ARCH1="x86_64" - ARCH2=$ARCH1 -else - if [[ $type == *o32* ]] || [[ $type == *i386* ]]; then - ARCH1="i386" - ARCH2="i686" - else - echo "unknown arch" - exit 1 - fi -fi - -if [[ $type == *++* ]]; then - COMPILER="clang++" -else - COMPILER="clang" -fi - -if [ $COMPILER == "clang++" ] && [[ $type == *libc++* ]]; then - USE_LIBCXX=1 -else - USE_LIBCXX=0 -fi - -TARGET=$ARCH1-apple-$OSXCROSS_TARGET -OSX_VERSION_MIN=$OSXCROSS_OSX_VERSION_MIN - -export COMPILER_PATH="$OSXCROSS_CCTOOLS_PATH:$COMPILER_PATH" - -if [ $# -gt 0 ]; then - for p in "$@" - do - if [[ "$p" == -mmacosx-version-min=* ]]; then - OSXCROSS_OSX_VERSION_MIN="default" - OSX_VERSION_MIN=`echo "$p" | tr '=' ' ' | awk '{print $2}'` - continue - fi - if [ "$p" == "-stdlib=libc++" ]; then - USE_LIBCXX=2 - continue - fi - if [ "$p" == "-v" ] || [ "$p" == "--version" ]; then - if [ $# -eq 1 ]; then - $COMPILER $OSXCROSS_TARGET_OPTION $TARGET $p - exit $? - fi - fi - done -else - $COMPILER $OSXCROSS_TARGET_OPTION $TARGET - exit $? -fi - -STDINC=$OSXCROSS_SDK/usr/include - -if [ $USE_LIBCXX -eq 0 ]; then - MIN_TARGET_VERSION=`echo "$OSX_VERSION_MIN" | tr '.' ' ' | awk '{printf "%d %d", $1, $2}'` - MIN_TARGET_VERSION_MAJOR=`echo $MIN_TARGET_VERSION | awk '{print $1}'` - MIN_TARGET_VERSION_MINOR=`echo $MIN_TARGET_VERSION | awk '{print $2}'` - - if [ $MIN_TARGET_VERSION_MAJOR -ge 10 ] && [ $MIN_TARGET_VERSION_MINOR -ge 9 ]; then - # default to libc++ on >= 10.9 - USE_LIBCXX=1 - fi -fi - -if [ $USE_LIBCXX -ne 0 ]; then - # -nostdinc++ to avoid using local libc++ headers... - CXXINC="-nostdinc++ -cxx-isystem $OSXCROSS_SDK/usr/include/c++/v1" - - if ( [ $OSX_VERSION_MIN != "default" ] && - [ `echo "$OSX_VERSION_MIN<=10.6" | bc -l` -eq 1 ] ); - then - if [ $OSXCROSS_OSX_VERSION_MIN = "default" ]; then - echo -en "\e[1mosxcross \e[31merror:\e[0m\e[0m" 2>&1 - echo " -stdlib=libc++ requires Mac OS X 10.7 or later" 2>&1 - exit 1 - fi - OSXCROSS_OSX_VERSION_MIN="10.7" - fi -else - CXXINC="-cxx-isystem $OSXCROSS_SDK/usr/lib/gcc/i686-apple-$OSXCROSS_TARGET/4.2.1/include" - CXXINC="$CXXINC -cxx-isystem $OSXCROSS_SDK/usr/include/c++/4.0.0 " - CXXINC="$CXXINC -cxx-isystem $OSXCROSS_SDK/usr/include/c++/4.0.0/$ARCH2-apple-darwin9 " -fi - -if [ $OSXCROSS_OSX_VERSION_MIN != "default" ]; then - OSX_VERSION_MIN_OPT="-mmacosx-version-min=$OSXCROSS_OSX_VERSION_MIN" -else - OSX_VERSION_MIN_OPT="" -fi - -XMMINTRIN=`readlink "$STDINC/xmmintrin.h"` -if [ $? -eq 0 ] && [ ! -f "$XMMINTRIN" ]; then - pushd "${0%/*}" &>/dev/null - if [ -f "osxcross-fix-intrinsic-headers" ]; then - echo -e "\e[1mosxcross:\e[0m\e[35m fixing intrinsic symlinks...\e[0m" 1>&2 - ./osxcross-fix-intrinsic-headers $OSXCROSS_SDK || { echo -e "\e[31mfailed.\e[0m" 1>&2 && exit 1; } - echo -e "\e[1mdone.\e[0m" 1>&2 - else - echo -en "\e[1mosxcross \e[31merror:\e[0m\e[1m dead intrinsic link found -" 1>&2 - echo -e "please re-run ./build.sh\e[0m" 1>&2 - exit 1 - fi -fi - -$COMPILER \ - $OSXCROSS_TARGET_OPTION $TARGET -isysroot $OSXCROSS_SDK \ - $CXXINC -mlinker-version=$OSXCROSS_LINKER_VERSION \ - $OSXCROSS_OPT_ARGS ${1+"$@"} $OSX_VERSION_MIN_OPT - -exit $? diff --git a/oclang/test_libcxx.cpp b/oclang/test_libcxx.cpp index 7f4fa08..a9a0d2b 100644 --- a/oclang/test_libcxx.cpp +++ b/oclang/test_libcxx.cpp @@ -1,27 +1,6 @@ -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include -#include - #include int main() diff --git a/ogcc/ogcc b/ogcc/ogcc deleted file mode 100755 index 4145e49..0000000 --- a/ogcc/ogcc +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env bash - -pushd "${0%/*}" &>/dev/null -eval `./osxcross-env` -eval `./osxcross-conf` -popd &>/dev/null - -type=`basename $0` - -if [[ $type == *o64* ]] || [[ $type == *x86_64* ]]; then - ARCH="x86_64" - ARCHFLAG="-m64" -else - if [[ $type == *o32* ]] || [[ $type == *i386* ]]; then - ARCH="i386" - ARCHFLAG="-m32" - else - echo "unknown arch" - exit 1 - fi -fi - -if [[ $type == *++* ]]; then - COMPILER="$OSXCROSS_TARGET_DIR/bin/$ARCH-apple-$OSXCROSS_TARGET-base-g++" -else - COMPILER="$OSXCROSS_TARGET_DIR/bin/$ARCH-apple-$OSXCROSS_TARGET-base-gcc" -fi - -OSX_VERSION_MIN=$OSXCROSS_OSX_VERSION_MIN - -if [ $# -gt 0 ]; then - ARCHGIVEN=0 - - for p in "$@" - do - if [ "$p" == "-arch" ] || [ "$p" == "-m32" ] || [ "$p" == "-m64" ]; then - ARCHGIVEN=1 - continue - fi - if [[ "$p" == -mmacosx-version-min=* ]]; then - OSXCROSS_OSX_VERSION_MIN="default" - OSX_VERSION_MIN=`echo "$p" | tr '=' ' ' | awk '{print $2}'` - continue - fi - if [ "$p" == "-v" ] || [ "$p" == "--version" ]; then - if [ $# -eq 1 ]; then - $COMPILER $p - exit $? - fi - fi - done - - if [ $ARCHGIVEN -eq 0 ]; then - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS $ARCHFLAG" - fi -fi - -if [[ $COMPILER == *g++ ]] && [[ $type == *libc++* ]]; then - if ( [ $OSX_VERSION_MIN != "default" ] && - [ `echo "$OSX_VERSION_MIN<=10.6" | bc -l` -eq 1 ] ); - then - if [ $OSXCROSS_OSX_VERSION_MIN = "default" ]; then - echo -en "\e[1mosxcross \e[31merror:\e[0m\e[0m" 2>&1 - echo " -stdlib=libc++ requires Mac OS X 10.7 or later" 2>&1 - exit 1 - fi - OSXCROSS_OSX_VERSION_MIN="10.7" - fi - - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS -std=c++0x -nostdinc++ -nodefaultlibs" - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS -lc -isystem $OSXCROSS_SDK/usr/include/c++/v1" - - REGEX="\-o\s*[^ ]+\.gch" - if [[ ! "$@" =~ $REGEX ]]; then - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS -lc++ -lc++abi -lgcc_s.10.5" - fi -else - REGEX="\-o\s*[^ ]+\.gch" - if [[ ! "$@" =~ $REGEX ]]; then - if [[ $COMPILER == *g++ ]]; then - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS -static-libgcc -static-libstdc++" - else - OSXCROSS_OPT_ARGS="$OSXCROSS_OPT_ARGS -static-libgcc" - fi - fi -fi - -if [ $OSXCROSS_OSX_VERSION_MIN != "default" ]; then - OSX_VERSION_MIN_OPT="-mmacosx-version-min=$OSXCROSS_OSX_VERSION_MIN" -else - OSX_VERSION_MIN_OPT="" -fi - -export COMPILER_PATH="$OSXCROSS_CCTOOLS_PATH:$COMPILER_PATH" -export LD_LIBRARY_PATH="/usr/local/lib:/usr/pkg/lib:$LD_LIBRARY_PATH" - -$COMPILER $OSX_VERSION_MIN_OPT $OSXCROSS_OPT_ARGS ${1+"$@"} - -exit $? diff --git a/package.sh b/package.sh index a76364a..54032f3 100755 --- a/package.sh +++ b/package.sh @@ -11,11 +11,14 @@ else BINARYPACKAGE="0" fi -TMPDIR=`mktemp -d` +TMPDIR=`mktemp -d /tmp/XXXXXXXXX` BASEDIR=`pwd` +set +e REVHASH=`git rev-parse --short HEAD` +set -e + OSXCROSSVER=`cat build.sh | grep "OSXCROSS_VERSION" | head -n1 | tr '=' ' ' | awk '{print $2}'` pushd $TMPDIR @@ -53,7 +56,7 @@ else echo "" >> $READMEINSTALL fi -find $BASEDIR -maxdepth 1 -type f -print0 | xargs -0 -i cp {} . +find $BASEDIR -maxdepth 1 -type f -exec cp {} . \; if [ $BINARYPACKAGE == "1" ]; then rm -f *.sh diff --git a/tools/tools.sh b/tools/tools.sh old mode 100644 new mode 100755 index 2a7ada9..8e2b8e8 --- a/tools/tools.sh +++ b/tools/tools.sh @@ -9,15 +9,19 @@ export CXX=clang++ # enable debug messages test -n "$OCDEBUG" && set -x -# how many concurrent jobs should be used for compiling? -JOBS=`tools/get_cpu_count.sh` +PSCRIPT="`basename $0`" -if [ "`basename $0`" != "build.sh" ]; then - `tools/osxcross_conf.sh` +if [[ $PSCRIPT != *wrapper/build.sh ]]; then + # how many concurrent jobs should be used for compiling? + JOBS=`tools/get_cpu_count.sh` - if [ $? -ne 0 ]; then - echo "you need to complete ./build.sh first, before you can start building $DESC" - exit 1 + if [ $PSCRIPT != "build.sh" ]; then + `tools/osxcross_conf.sh` + + if [ $? -ne 0 ]; then + echo "you need to complete ./build.sh first, before you can start building $DESC" + exit 1 + fi fi fi @@ -78,6 +82,12 @@ function extract() fi } +function verbose_cmd() +{ + echo "$@" + eval "$@" +} + function test_compiler() { echo -ne "testing $1 ... " diff --git a/tools/trap_exit.sh b/tools/trap_exit.sh old mode 100644 new mode 100755 diff --git a/wrapper/build.sh b/wrapper/build.sh new file mode 100755 index 0000000..a5cdf52 --- /dev/null +++ b/wrapper/build.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +pushd "${0%/*}" &>/dev/null +pushd .. &>/dev/null +source ./tools/tools.sh +popd &>/dev/null + +EXESUFFIX="" + +function create_wrapper_link +{ + verbose_cmd ln -sf "${TARGETTRIPLE}-wrapper${EXESUFFIX}" "${1}${EXESUFFIX}" +} + +[ -z "$TARGET" ] && TARGET=darwin12 +[ -z "$OSX_VERSION_MIN" ] && OSX_VERSION_MIN=10.5 +[ -z "$LINKER_VERSION" ] && LINKER_VERSION=134.9 +[ -z "$TARGETCOMPILER" ] && TARGETCOMPILER=clang + +TARGETTRIPLE=x86_64-apple-$TARGET + +FLAGS="" + +if [ -n "$BWPLATFORM" ]; then + PLATFORM=$BWPLATFORM + + if [ $PLATFORM = "Darwin" -a $(uname -s) != "Darwin" ]; then + CXX=o32-clang++ + elif [ $PLATFORM = "FreeBSD" -a $(uname -s) != "FreeBSD" ]; then + CXX=amd64-pc-freebsd10.0-clang++ + FLAGS+="-lrt " + elif [ $PLATFORM = "Windows" ]; then + CXX=w32-clang++ + FLAGS+="-wc-static-runtime -g " + EXESUFFIX=".exe" + elif [ $PLATFORM = "MWindows" ]; then + CXX=i686-w64-mingw32-g++ + FLAGS+="-static-libgcc -static-libstdc++ -g " + EXESUFFIX=".exe" + fi +else + PLATFORM=$(uname -s) + FLAGS="-march=native " +fi + +if [ -n "$BWCXX" ]; then + [ "$CXX" != "$BWCXX" ] && echo "using $BWCXX" 1>&2 + CXX=$BWCXX +fi + +[ $PLATFORM = "Darwin" ] && FLAGS+="-framework CoreServices " +[ $PLATFORM = "FreeBSD" ] && FLAGS+="-lutil " + +if [[ $PLATFORM != *Windows ]] && [ $PLATFORM != "Darwin" ]; then + FLAGS+="-lrt " +fi + +function compile_wrapper() +{ + mkdir -p ../target ../target/bin + + verbose_cmd $CXX compiler.cpp -std=c++0x -pedantic -Wall -Wextra \ + "-DOSXCROSS_TARGET=\"\\\"$TARGET\\\"\"" \ + "-DOSXCROSS_OSX_VERSION_MIN=\"\\\"$OSX_VERSION_MIN\\\"\"" \ + "-DOSXCROSS_LINKER_VERSION=\"\\\"$LINKER_VERSION\\\"\"" \ + -o "../target/bin/${TARGETTRIPLE}-wrapper${EXESUFFIX}" -O2 \ + $FLAGS $* +} + +compile_wrapper + +pushd "../target/bin" &>/dev/null + +if [ $TARGETCOMPILER = "clang" ]; then + create_wrapper_link o32-clang + create_wrapper_link o32-clang++ + create_wrapper_link o32-clang++-libc++ + + create_wrapper_link o64-clang + create_wrapper_link o64-clang++ + create_wrapper_link o64-clang++-libc++ + + create_wrapper_link i386-apple-$TARGET-clang + create_wrapper_link i386-apple-$TARGET-clang++ + create_wrapper_link i386-apple-$TARGET-clang++-libc++ + + create_wrapper_link x86_64-apple-$TARGET-clang + create_wrapper_link x86_64-apple-$TARGET-clang++ + create_wrapper_link x86_64-apple-$TARGET-clang++-libc++ +elif [ $TARGETCOMPILER = "gcc" ]; then + create_wrapper_link o32-gcc + create_wrapper_link o32-g++ + create_wrapper_link o32-g++-libc++ + + create_wrapper_link o64-gcc + create_wrapper_link o64-g++ + create_wrapper_link o64-g++-libc++ + + create_wrapper_link i386-apple-$TARGET-gcc + create_wrapper_link i386-apple-$TARGET-g++ + create_wrapper_link i386-apple-$TARGET-g++-libc++ + + create_wrapper_link x86_64-apple-$TARGET-gcc + create_wrapper_link x86_64-apple-$TARGET-g++ + create_wrapper_link x86_64-apple-$TARGET-g++-libc++ +fi + +create_wrapper_link i386-apple-$TARGET-cc +create_wrapper_link i386-apple-$TARGET-c++ + +create_wrapper_link x86_64-apple-$TARGET-cc +create_wrapper_link x86_64-apple-$TARGET-c++ + +create_wrapper_link osxcross-conf +create_wrapper_link osxcross-env + +create_wrapper_link sw_vers +create_wrapper_link i386-apple-$TARGET-sw_vers +create_wrapper_link x86_64-apple-$TARGET-sw_vers + +create_wrapper_link dsymutil +create_wrapper_link i386-apple-$TARGET-dsymutil +create_wrapper_link x86_64-apple-$TARGET-dsymutil + +popd &>/dev/null +popd &>/dev/null diff --git a/wrapper/compat.h b/wrapper/compat.h new file mode 100644 index 0000000..a0426e7 --- /dev/null +++ b/wrapper/compat.h @@ -0,0 +1,15 @@ +#ifdef __clang__ +#if __has_include(<__config>) +#include <__config> +#endif + +#ifndef _LIBCPP_CONFIG +// NetBSD's libstdc++ 4.5.3 +// requires this +#undef __GXX_EXPERIMENTAL_CXX0X__ +#endif +#endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif diff --git a/wrapper/compiler.cpp b/wrapper/compiler.cpp new file mode 100644 index 0000000..68ab42e --- /dev/null +++ b/wrapper/compiler.cpp @@ -0,0 +1,1807 @@ +/*********************************************************************** + * OSXCross * + * Copyright (C) 2013, 2014 by Thomas Poechtrager * + * t.poechtrager@gmail.com * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***********************************************************************/ + +/* + * Important: + * - Avoid the use of C++11 headers + * - Avoid std:: for C functions + * + * Any other C++11 features can be used as long they are supported + * by Clang 3.2. + * + * Debug messages can be enabled by setting 'OCDEBUG' (ENV) to 1. + * + * TODO: + * - handle MACOSX_DEPLOYMENT_TARGET (env) + * + */ + +#include "compat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#endif + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#endif + +#ifdef __FreeBSD__ +#include +#include +#include +#include +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include "oscompat.h" + +#undef check +#undef major +#undef minor +#undef patch + +namespace { + +// +// Misc helper tools +// + +typedef std::vector string_vector; + +char *getExecutablePath(char *buf, size_t len) { + char *p; +#ifdef __APPLE__ + unsigned int l = len; + if (_NSGetExecutablePath(buf, &l) != 0) + return nullptr; +#elif defined(__FreeBSD__) + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + size_t l = len; + if (sysctl(mib, 4, buf, &l, nullptr, 0) != 0) + return nullptr; +#elif defined(_WIN32) + size_t l = GetModuleFileName(nullptr, buf, (DWORD)len); +#else + ssize_t l = readlink("/proc/self/exe", buf, len); +#endif + if (l <= 0) + return nullptr; + buf[len - 1] = '\0'; + p = strrchr(buf, '/'); + if (*p) + *p = '\0'; + return buf; +} + +__attribute__((unused)) std::string &fixPathDiv(std::string &path) { +#ifdef _WIN32 + for (auto &c : path) { + if (c == '/') { + c = '\\'; + } + } +#else +// let's assume the compiler is smart enough +// to optimize this function call away +#endif + return path; +} + +__attribute__((unused)) std::string *getFileContent(const std::string &file, + std::string &content) { + std::ifstream f(file.c_str()); + + if (!f.is_open()) + return nullptr; + + f.seekg(0, std::ios::end); + auto len = f.tellg(); + f.seekg(0, std::ios::beg); + + if (len != static_cast(-1)) + content.reserve(static_cast(f.tellg())); + + content.assign(std::istreambuf_iterator(f), + std::istreambuf_iterator()); + + return &content; +} + +const std::string &getParentProcessName() { + static std::string name; +#ifdef _WIN32 + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe; + + auto zerope = [&]() { + memset(&pe, 0, sizeof(pe)); + pe.dwSize = sizeof(PROCESSENTRY32); + }; + + zerope(); + + auto pid = GetCurrentProcessId(); + decltype(pid) ppid = -1; + + if (Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + ppid = pe.th32ParentProcessID; + break; + } + } while (Process32Next(h, &pe)); + } + + if (ppid != static_cast(-1)) { + PROCESSENTRY32 *ppe = nullptr; + zerope(); + + if (Process32First(h, &pe)) { + do { + std::cout << pe.szExeFile << " " << pe.th32ProcessID << std::endl; + if (pe.th32ProcessID == ppid) { + ppe = &pe; + break; + } + } while (Process32Next(h, &pe)); + } + + if (ppe) { + char *p = strrchr(ppe->szExeFile, '\\'); + if (p) { + name = p + 1; + } else { + name = ppe->szExeFile; + } + } + } + + CloseHandle(h); + + if (!name.empty()) { + return name; + } +#else + auto getName = [](const char * path)->const char * { + if (const char *p = strrchr(path, '/')) { + return p + 1; + } + return path; + }; + auto ppid = getppid(); +#ifdef __APPLE__ + char path[PROC_PIDPATHINFO_MAXSIZE]; + if (proc_pidpath(ppid, path, sizeof(path))) { + name = getName(path); + return name; + } +#elif defined(__FreeBSD__) + struct kinfo_proc *proc = kinfo_getproc(ppid); + if (proc) { + name = getName(proc->ki_comm); + free(proc); + return name; + } +#else + std::stringstream file; + file << "/proc/" << ppid << "/comm"; + if (getFileContent(file.str(), name)) { + if (!name.empty() && name.rbegin()[0] == '\n') { + name.resize(name.size() - 1); + } + return name; + } else { + file.str(std::string()); + file << "/proc/" << ppid << "/exe"; + char buf[PATH_MAX + 1]; + if (readlink(file.str().c_str(), buf, sizeof(buf)) > 0) { + buf[PATH_MAX] = '\0'; + name = getName(buf); + return name; + } + } +#endif +#endif + name = "unknown"; + return name; +} + +void concatEnvVariable(const char *var, const std::string val) { + std::string nval = val; + if (char *oldval = getenv(var)) { + nval += ":"; + nval += oldval; + } + setenv(var, nval.c_str(), 1); +} + +bool dirExists(const std::string &dir) { + struct stat st; + return !stat(dir.c_str(), &st) && S_ISDIR(st.st_mode); +} + +typedef bool (*listfilescallback)(const char *file); + +bool isDirectory(const char *file, const char *prefix = nullptr) { + struct stat st; + if (prefix) { + std::string tmp = prefix; + tmp += "/"; + tmp += file; + return !stat(tmp.c_str(), &st) && S_ISDIR(st.st_mode); + } else { + return !stat(file, &st) && S_ISDIR(st.st_mode); + } +} + +bool listFiles(const char *dir, std::vector *files, + listfilescallback cmp = nullptr) { +#ifndef _WIN32 + DIR *d = opendir(dir); + dirent *de; + + if (!d) + return false; + + while ((de = readdir(d))) { + if ((!cmp || cmp(de->d_name)) && files) { + files->push_back(de->d_name); + } + } + + closedir(d); + return true; +#else +#warning TODO + (void)dir; + (void)files; + (void)cmp; + return false; +#endif +} + +typedef bool (*realpathcmp)(const char *file, const struct stat &st); + +bool isExecutable(const char *f, const struct stat &) { + return !access(f, F_OK | X_OK); +} + +std::string &realPath(const char *file, std::string &result, + realpathcmp cmp = nullptr) { + char *PATH = getenv("PATH"); + const char *p = PATH; + std::string sfile; + struct stat st; + + assert(PATH); + + do { + if (*p == ':') { + ++p; + } + + while (*p && *p != ':') { + sfile += *p++; + } + + sfile += "/"; + sfile += file; + + if (!stat(sfile.c_str(), &st) && (!cmp || cmp(sfile.c_str(), st))) { + break; + } + + sfile.clear(); + } while (*p); + +#ifndef _WIN32 + if (!sfile.empty()) { + char buf[PATH_MAX + 1]; + ssize_t len; + + if ((len = readlink(sfile.c_str(), buf, PATH_MAX)) != -1) { + result.assign(buf, len); + } + } +#endif + + result.swap(sfile); + return result; +} + +std::string &getPathOfCommand(const char *command, std::string &result) { + realPath(command, result, isExecutable); + + const size_t len = strlen(command) + 1; + + if (result.size() < len) { + result.clear(); + return result; + } + + result.resize(result.size() - len); + return result; +} + +typedef unsigned long long time_type; + +time_type getNanoSeconds() { +#ifdef __APPLE__ + union { + AbsoluteTime at; + time_type ull; + } tmp; + tmp.ull = mach_absolute_time(); + Nanoseconds ns = AbsoluteToNanoseconds(tmp.at); + tmp.ull = UnsignedWideToUInt64(ns); + return tmp.ull; +#elif defined(__linux__) + struct timespec tp; + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) + return static_cast((tp.tv_sec * 1000000000LL) + tp.tv_nsec); +#endif + struct timeval tv; + if (gettimeofday(&tv, nullptr) == 0) + return static_cast((tv.tv_sec * 1000000000LL) + + (tv.tv_usec * 1000)); + abort(); +} + +class benchmark { +public: + benchmark() { s = getTime(); } + + time_type getDiff() { return getTime() - s; } + + void halt() { + h = getTime(); + } + + void resume() { + s += getTime() - h; + } + + ~benchmark() { + time_type diff = getTime() - s; + std::cerr << "took: " << diff / 1000000.0 << " ms" << std::endl; + } + +private: + time_type getTime() { + return getNanoSeconds(); + } + time_type h; + time_type s; +}; + +// +// OSVersion struct to ease OS Version comparison +// + +struct OSVersion { + constexpr OSVersion(int major, int minor, int patch = 0) + : major(major), minor(minor), patch(patch) {} + constexpr OSVersion() : major(), minor(), patch() {} + + constexpr int Num() const { + return major * 10000 + minor * 100 + patch; + }; + + constexpr bool operator>(const OSVersion &OSNum) const { + return Num() > OSNum.Num(); + } + + constexpr bool operator>=(const OSVersion &OSNum) const { + return Num() >= OSNum.Num(); + } + + constexpr bool operator<(const OSVersion &OSNum) const { + return Num() < OSNum.Num(); + } + + constexpr bool operator<=(const OSVersion &OSNum) const { + return Num() <= OSNum.Num(); + } + + constexpr bool operator!=(const OSVersion &OSNum) const { + return Num() != OSNum.Num(); + } + + bool operator!=(const char *val) const { + size_t c = 0; + const char *p = val; + + while (*p) { + if (*p++ == '.') + ++c; + } + switch (c) { + case 1: + return shortStr() != val; + case 2: + return Str() != val; + default: + return true; + } + } + + std::string Str() const { + std::stringstream tmp; + tmp << major << "." << minor << "." << patch; + return tmp.str(); + } + + std::string shortStr() const { + std::stringstream tmp; + tmp << major << "." << minor; + return tmp.str(); + } + + int major; + int minor; + int patch; +}; + +static_assert(OSVersion(10, 6) != OSVersion(10, 5), ""); + +OSVersion parseOSVersion(const char *OSVer) { + const char *p = OSVer; + OSVersion OSNum; + + OSNum.major = atoi(p); + + while (*p && *p++ != '.') + ; + if (!*p) + return OSNum; + + OSNum.minor = atoi(p); + if (!*p) + return OSNum; + + while (*p && *p++ != '.') + ; + if (!*p) + return OSNum; + + OSNum.patch = atoi(p); + return OSNum; +} + +typedef OSVersion GCCVersion; +#define parseGCCVersion parseOSVersion + +typedef OSVersion ClangVersion; +#define parseClangVersion parseOSVersion + +// +// Default values for the Target struct +// + +constexpr const char *getDefaultVendor() { return "apple"; } +constexpr const char *getDefaultTarget() { return OSXCROSS_TARGET; } +constexpr const char *getDefaultCompiler() { return "clang"; } +constexpr const char *getDefaultCXXCompiler() { return "clang++"; } +constexpr const char *getLinkerVersion() { return OSXCROSS_LINKER_VERSION; } + +constexpr const char *getLibLTOPath() { +#ifdef OSXCROSS_LIBLTO_PATH + return OSXCROSS_LIBLTO_PATH; +#else + return nullptr; +#endif +} + +const char *getDefaultCStandard() { return getenv("OSXCROSS_C_STANDARD"); } +const char *getDefaultCXXStandard() { return getenv("OSXCROSS_CXX_STANDARD"); } + +#ifdef OSXCROSS_OSX_VERSION_MIN +OSVersion getDefaultMinTarget() { + if (!strcmp(OSXCROSS_OSX_VERSION_MIN, "default")) + return OSVersion(); + + return parseOSVersion(OSXCROSS_OSX_VERSION_MIN); +} +#else +constexpr OSVersion getDefaultMinTarget() { return OSVersion(); } +#endif + +// +// Machine +// + +enum Machine { + x86_64, + i386, + unknown +}; + +constexpr const char *MachineStrs[] = { "x86_64", "i386", "unknown" }; +constexpr const char *MachineStrs2[] = { "x86_64", "i686", "unknown" }; + +constexpr const char *getMachineString(Machine machine) { + return MachineStrs[machine]; +} + +constexpr const char *getMachineString2(Machine machine) { + return MachineStrs2[machine]; +} + +Machine parseMachine(const char *machine) { + if (!strcmp(machine, "i386") || !strcmp(machine, "i486") || + !strcmp(machine, "i586") || !strcmp(machine, "i686")) { + return Machine::i386; + } else if (!strcmp(machine, "x86_64")) { + return Machine::x86_64; + } + return Machine::unknown; +} + +// +// Stdlib +// + +enum StdLib { + unset, + libcxx, + libstdcxx +}; + +constexpr const char *StdLibStrs[] = { "default", "libc++", "libstdc++" }; + +constexpr const char *getStdLibString(StdLib stdlib) { + return StdLibStrs[stdlib]; +} + +// +// Target struct +// + +struct Target { + Target() + : vendor(getDefaultVendor()), target(getDefaultTarget()), + stdlib(StdLib::unset), usegcclibs(), compiler(getDefaultCompiler()), + lang(), langstd(), sourcefile(), outputname() { + if (!getExecutablePath(execpath, sizeof(execpath))) + abort(); + } + + OSVersion getSDKOSNum() const { + if (target.size() < 7) + return OSVersion(); + + int n = atoi(target.c_str() + 6); + return OSVersion(10, 4 + (n - 8)); + } + + bool getSDKPath(std::string &path) const { + OSVersion SDKVer = getSDKOSNum(); + path = execpath; + path += "/../SDK/MacOSX"; + path += SDKVer.shortStr(); + if (SDKVer <= OSVersion(10, 4)) { + path += "u"; + } + path += ".sdk"; + return dirExists(path); + } + + void addMachine(const Machine machine) { + auto &v = targetmachine; + for (size_t i = 0; i < v.size(); ++i) { + if (v[i] == machine) { + v.erase(v.begin() + i); + addMachine(machine); + return; + } + } + v.push_back(machine); + } + + bool hasLibCXX() const { return getSDKOSNum() >= OSVersion(10, 7); } + + bool libCXXIsDefaultCXXLib() const { + return stdlib != libstdcxx && hasLibCXX() && OSNum >= OSVersion(10, 9); + } + + bool isLibCXX() const { + return stdlib == StdLib::libcxx || libCXXIsDefaultCXXLib(); + } + + bool isLibSTDCXX() const { return stdlib == StdLib::libstdcxx; } + + bool haveSourceFile() { return sourcefile != nullptr; } + bool haveOutputName() { return outputname != nullptr; } + + bool isC(bool r = false) { + if (!r && isCXX()) + return false; + + if (langGiven() && lang[0] != 'o' && + (!strcmp(lang, "c") || !strcmp(lang, "c-header"))) + return true; + + if (haveSourceFile()) { + const char *ext = strrchr(sourcefile, '.'); + + if (ext && !strcmp(ext, ".c")) + return true; + } + + return compiler.find("++") == std::string::npos && !isObjC(true); + } + + bool isCXX() { + bool CXXCompiler = compiler.find("++") != std::string::npos; + + if (!langGiven() && CXXCompiler && !isObjC(true)) + return true; + + if (langGiven() && !strncmp(lang, "c++", 3)) + return true; + + constexpr const char *CXXFileExts[] = { ".C", ".cc", ".cpp", ".CPP", + ".c++", ".cp", ".cxx" }; + + if (haveSourceFile()) { + const char *ext = strrchr(sourcefile, '.'); + + if (ext) { + for (auto &cxxfe : CXXFileExts) { + if (!strcmp(ext, cxxfe)) + return true; + } + } + } + + return CXXCompiler && !isC(true) && !isObjC(true); + } + + bool isObjC(bool r = false) { + if (!r && isCXX()) + return false; + + if (langGiven() && lang[0] == 'o') + return true; + + if (haveSourceFile()) { + const char *ext = strrchr(sourcefile, '.'); + + if (ext && (!strcmp(ext, ".m") || !strcmp(ext, ".mm"))) + return true; + } + + return false; + } + + bool isGCH() { + if (haveOutputName()) { + const char *ext = strrchr(outputname, '.'); + + if (!ext) + return false; + + return !strcmp(ext, ".gch"); + } + return false; + } + + bool isClang() const { return !compiler.compare(0, 4, "clang", 4); } + + bool isGCC() const { + return !compiler.compare(0, 3, "gcc") || !compiler.compare(0, 3, "g++"); + } + + bool isKnownCompiler() const { return isClang() || isGCC(); } + + bool langGiven() const { return lang != nullptr; } + bool langStdGiven() const { return langstd != nullptr; } + + const char *getLangName() { + if (isC()) + return "C"; + else if (isCXX()) + return "C++"; + else if (isObjC()) + return "Obj-C"; + else + return "unknown"; + } + + bool isCXX11orNewer() const { + if (!langStdGiven()) + return false; + + constexpr const char *STD[] = { "c++0x", "gnu++0x", "c++11", "gnu++11", + "c++1y", "gnu++1y", "c++14", "gnu++14", + "c++1z", "gnu++1z" }; + + for (auto std : STD) { + if (!strcmp(langstd, std)) { + return true; + } + } + + return false; + } + + const std::string &getTriple() const { return triple; } + + const std::string getFullCompilerName() const { + std::string compiler; + + if (isGCC()) { + compiler = execpath; + compiler += "/"; + compiler += getTriple(); + compiler += "-"; + } + + if (isGCC()) { + compiler += "base-"; + } + + compiler += this->compiler; + return compiler; + } + + bool findClangIntrinsicHeaders(std::string &path) const { + std::string clangbin; + static std::stringstream dir; + + assert(isClang()); + + getPathOfCommand(compiler.c_str(), clangbin); + + if (clangbin.empty()) + return false; + + static ClangVersion clangversion; + static std::string pathtmp; + + dir.str(std::string()); + clangversion = ClangVersion(); + pathtmp.clear(); + + auto check = []()->bool { + + listFiles(dir.str().c_str(), nullptr, [](const char *file) { + + if (file[0] != '.' && isDirectory(file, dir.str().c_str())) { + ClangVersion cv = parseClangVersion(file); + + if (cv != ClangVersion()) { + static std::stringstream tmp; + tmp.str(std::string()); + + tmp << dir.str() << "/" << file << "/include"; + + if (dirExists(tmp.str())) { + if (cv > clangversion) { + clangversion = cv; + pathtmp = tmp.str(); + } + } + } + + return true; + } + + return true; + }); + + return clangversion != ClangVersion(); + }; + + dir << clangbin << "/../lib/clang"; + + if (!check()) { + dir.str(std::string()); + dir << clangbin << "/../include/clang"; + + if (!check()) { + return false; + } + } + + path.swap(pathtmp); + return clangversion != ClangVersion(); + } + + bool Setup() { + if (!isKnownCompiler()) { + std::cerr << "warning: unknown compiler '" << compiler << "'" + << std::endl; + } + + std::string SDKPath; + if (!getSDKPath(SDKPath)) { + std::cerr << "cannot find Mac OS X SDK (expected in: " << SDKPath << ")" + << std::endl; + return false; + } + + if (targetmachine.empty()) { + targetmachine.push_back(machine); + } + + if (!langStdGiven()) { + if (isC()) { + langstd = getDefaultCStandard(); + } else if (isCXX()) { + langstd = getDefaultCXXStandard(); + } + } + + triple = getMachineString(machine); + triple += "-"; + triple += vendor; + triple += "-"; + triple += target; + + otriple = getMachineString(Machine::x86_64); + otriple += "-"; + otriple += vendor; + otriple += "-"; + otriple += target; + + if (!OSNum.Num()) { + if (stdlib != StdLib::libcxx) { + OSNum = getDefaultMinTarget(); + } else { + OSNum = OSVersion(10, 7); // Hack + } + } else { + if (OSNum > getSDKOSNum()) { + std::cerr << "targeted OS X Version must be <= " << getSDKOSNum().Str() + << " (SDK)" << std::endl; + return false; + } else if (OSNum < OSVersion(10, 4)) { + std::cerr << "targeted OS X Version must be >= 10.4" << std::endl; + return false; + } + } + + if (stdlib == StdLib::unset) { + if (libCXXIsDefaultCXXLib()) + stdlib = StdLib::libcxx; + else + stdlib = StdLib::libstdcxx; + } else if (stdlib == StdLib::libcxx) { + if (!hasLibCXX()) { + std::cerr + << "you need a newer SDK (10.7 at least) if you want to use libc++" + << std::endl; + return false; + } + + if (OSNum.Num() && OSNum < OSVersion(10, 7)) { + std::cerr + << "you must target OS X 10.7 or newer if you want to use libc++" + << std::endl; + return false; + } + } + + std::string CXXHeaderPath = SDKPath; + string_vector AdditionalCXXHeaderPaths; + + auto addCXXPath = [&](const std::string &path) { + std::string tmp; + tmp = CXXHeaderPath; + tmp += "/"; + tmp += path; + AdditionalCXXHeaderPaths.push_back(tmp); + }; + + auto addAbsoluteCXXPath = [&](const std::string &path) { + AdditionalCXXHeaderPaths.push_back(path); + }; + + (void)addAbsoluteCXXPath; + + GCCVersion gccversion; + + switch (stdlib) { + case StdLib::libcxx: { + CXXHeaderPath += "/usr/include/c++/v1"; + if (!dirExists(CXXHeaderPath)) { + std::cerr << "cannot find " << getStdLibString(stdlib) << " headers" + << std::endl; + return false; + } + break; + } + case StdLib::libstdcxx: { + if (isGCC() && /*isCXX11orNewer()*/ true) + break; + + if (usegcclibs) { +#ifndef _WIN32 + // Use libs from './build_gcc' installation + + CXXHeaderPath += "/../../"; + CXXHeaderPath += otriple; + CXXHeaderPath += "/include/c++"; + + static std::vector v; + v.clear(); + + listFiles(CXXHeaderPath.c_str(), nullptr, [](const char *path) { + if (path[0] != '.') + v.push_back(parseGCCVersion(path)); + return false; + }); + + if (v.empty()) { + std::cerr << "'-oc-use-gcc-libs' requires gcc to be installed " + "(./build_gcc.sh)" << std::endl; + return false; + } + + std::sort(v.begin(), v.end()); + gccversion = v[v.size() - 1]; + + CXXHeaderPath += "/"; + CXXHeaderPath += gccversion.Str(); + + addCXXPath(otriple); +#else + std::cerr << "'-oc-use-gcc-libs' not implemented" << std::endl; + return false; +#endif + } else { + // Use SDK libs + std::string tmp; + + if (getSDKOSNum() <= OSVersion(10, 5)) { + CXXHeaderPath += "/usr/include/c++/4.0.0"; + } else { + CXXHeaderPath += "/usr/include/c++/4.2.1"; + } + + tmp = getMachineString2(machine); + tmp += "-apple-"; + tmp += target; + addCXXPath(tmp); + } + + if (!dirExists(CXXHeaderPath)) { + std::cerr << "cannot find " << getStdLibString(stdlib) << " headers" + << std::endl; + return false; + } + + break; + } + case StdLib::unset: + abort(); + } + + fargs.push_back(getFullCompilerName()); + + if (isClang()) { + std::string tmp; + + fargs.push_back("-target"); + fargs.push_back(getTriple()); + + tmp = "-mlinker-version="; + tmp += getLinkerVersion(); + + fargs.push_back(tmp); + tmp.clear(); + + if (!findClangIntrinsicHeaders(tmp)) { + std::cerr << "cannot find clang intrinsic headers, please report this " + "issue to the OSXCross project" << std::endl; + } else { + fargs.push_back("-isystem"); + fargs.push_back(tmp); + } + + tmp.clear(); + + fargs.push_back("-isysroot"); + fargs.push_back(SDKPath); + + if (isCXX()) { + tmp = "-stdlib="; + tmp += getStdLibString(stdlib); + fargs.push_back(tmp); + + if (stdlib == StdLib::libcxx || + (stdlib == StdLib::libstdcxx && usegcclibs)) { + fargs.push_back("-nostdinc++"); + // TODO: -Qunused-arguments ? + } + + if (stdlib == StdLib::libstdcxx && usegcclibs) { + // Use libs from './build_gcc' installation + + if (targetmachine.size() > 1) { + std::cerr + << "'-oc-use-gcc-libs' does not support multiple arch flags" + << std::endl; + return false; + } + + fargs.push_back("-nodefaultlibs"); + + std::stringstream GCCLibSTDCXXPath; + std::stringstream GCCLibPath; + std::stringstream tmp; + + GCCLibSTDCXXPath << SDKPath << "/../../" << otriple << "/lib"; + GCCLibPath << SDKPath << "/../../lib/gcc/" << otriple << "/" + << gccversion.Str(); + + if (targetmachine[0] == Machine::i386) { + GCCLibSTDCXXPath << "/" << getMachineString(Machine::i386); + GCCLibPath << "/" << getMachineString(Machine::i386); + } + + fargs.push_back("-Qunused-arguments"); + + tmp << GCCLibSTDCXXPath.str() << "/libstdc++.a"; + fargs.push_back(tmp.str()); + + tmp.str(std::string()); + tmp << GCCLibSTDCXXPath.str() << "/libsupc++.a"; + fargs.push_back(tmp.str()); + + tmp.str(std::string()); + tmp << GCCLibPath.str() << "/libgcc.a"; + fargs.push_back(tmp.str()); + + tmp.str(std::string()); + tmp << GCCLibPath.str() << "/libgcc_eh.a"; + fargs.push_back(tmp.str()); + + fargs.push_back("-lc"); + } + } + } else if (isGCC()) { + + if (isLibCXX()) { + if (!langStdGiven()) + langstd = "c++0x"; + else if (!isCXX11orNewer()) { + std::cerr << "warning: libc++ requires -std=c++11 (or later) with gcc" + << std::endl; + } + } + + /* TODO: libgcc */ + + if (isCXX() && (/*!isCXX11orNewer() ||*/ isLibCXX())) { + fargs.push_back("-nostdinc++"); + fargs.push_back("-nodefaultlibs"); + + if (haveSourceFile() && !isGCH()) { + std::string tmp; + + tmp = "-L"; + tmp += SDKPath; + tmp += "/usr/lib"; + + fargs.push_back(tmp); + fargs.push_back("-lc"); + + if (isLibCXX()) { + fargs.push_back("-lc++"); + fargs.push_back("-lc++abi"); + } else if (isLibSTDCXX()) { + // Hack: Use SDKs libstdc++ as long + // >= -std=c++11 is not given. + + fargs.push_back("-lstdc++"); + } + + fargs.push_back(OSNum <= OSVersion(10, 4) ? "-lgcc_s.10.4" + : "-lgcc_s.10.5"); + } + } else if (!isLibCXX() /*&& isCXX11orNewer()*/ && !isGCH()) { + fargs.push_back("-static-libgcc"); + fargs.push_back("-static-libstdc++"); + } + } + + auto addCXXHeaderPath = [&](const std::string &path) { + fargs.push_back(isClang() ? "-cxx-isystem" : "-isystem"); + fargs.push_back(path); + }; + + addCXXHeaderPath(CXXHeaderPath); + + for (auto &path : AdditionalCXXHeaderPaths) + addCXXHeaderPath(path); + + if (langGiven()) { + fargs.push_back("-x"); + fargs.push_back(lang); + } + + if (langStdGiven()) { + std::string tmp; + tmp = "-std="; + tmp += langstd; + fargs.push_back(tmp); + } + + if (OSNum.Num()) { + std::string tmp; + tmp = "-mmacosx-version-min="; + tmp += OSNum.Str(); + fargs.push_back(tmp); + } + + for (auto machine : targetmachine) { + switch (machine) { + case Machine::i386: + case Machine::x86_64: + if (isGCC()) { + if (targetmachine.size() > 1) { + std::cerr << "gcc does not support multiple arch flags" + << std::endl; + return false; + } + fargs.push_back(machine == Machine::i386 ? "-m32" : "-m64"); + } else { + fargs.push_back("-arch"); + fargs.push_back(getMachineString(machine)); + } + break; + default: + std::cerr << "unknown machine type" << std::endl; + return false; + } + } + + if (haveOutputName()) { + fargs.push_back("-o"); + fargs.push_back(outputname); + } + + return true; + } + + const char *vendor; + Machine machine; + std::vector targetmachine; + std::string target; + OSVersion OSNum; + StdLib stdlib; + bool usegcclibs; + std::string compiler; + std::string triple; + std::string otriple; + const char *lang; + const char *langstd; + string_vector fargs; + string_vector args; + const char *sourcefile; + const char *outputname; + char execpath[PATH_MAX + 1]; +}; + +// +// Program 'sw_vers' +// + +__attribute__((noreturn)) void prog_sw_vers(int argc, char **argv) { + + auto genFakeBuildVer = [](std::string & build)->std::string & { + std::stringstream tmp; + +#if __has_builtin(__builtin_readcyclecounter) + srand(static_cast(__builtin_readcyclecounter())); +#else + srand(static_cast(getNanoSeconds())); +#endif + + for (int i = 0; i < 5; ++i) { + tmp << std::hex << (rand() % 16 + 1); + } + + build = tmp.str(); + build.resize(5); + + return build; + }; + + auto getProductVer = []()->OSVersion { + char *p = getenv("OSXCROSS_SW_VERS_OSX_VERSION"); + + if (!p) + p = getenv("MACOSX_DEPLOYMENT_TARGET"); + + if (p) + return parseOSVersion(p); + + return getDefaultMinTarget(); + }; + + if (argc == 2) { + std::stringstream str; + + if (!strcmp(argv[1], "-productName")) { + str << "Mac OS X"; + } else if (!strcmp(argv[1], "-productVersion")) { + str << getProductVer().shortStr(); + } else if (!strcmp(argv[1], "-buildVersion")) { + std::string build; + str << genFakeBuildVer(build); + } else { + exit(EXIT_FAILURE); + } + + std::cout << str.str() << std::endl; + } else if (argc == 1) { + std::string build; + + std::cout << "ProductName: Mac OS X" << std::endl; + std::cout << "ProductVersion: " << getProductVer().shortStr() << std::endl; + std::cout << "BuildVersion: " << genFakeBuildVer(build) << std::endl; + } + + exit(EXIT_SUCCESS); +} + +// +// Program 'osxcross-env' +// + +__attribute__((noreturn)) void prog_osxcross_conf(const Target &target) { + std::string sdkpath; + if (!target.getSDKPath(sdkpath)) { + std::cerr << "cannot find Mac OS X SDK!" << std::endl; + exit(EXIT_FAILURE); + } + + // TODO: echo "export OSXCROSS_VERSION=..." + + std::cout << "export OSXCROSS_OSX_VERSION_MIN=" + << getDefaultMinTarget().shortStr() << std::endl; + std::cout << "export OSXCROSS_TARGET=" << getDefaultTarget() << std::endl; + std::cout << "export OSXCROSS_SDK_VERSION=" << target.getSDKOSNum().shortStr() + << std::endl; + std::cout << "export OSXCROSS_SDK=" << sdkpath << std::endl; + std::cout << "export OSXCROSS_TARBALL_DIR=" << target.execpath + << "/../../tarballs" << std::endl; + std::cout << "export OSXCROSS_PATCH_DIR=" << target.execpath + << "/../../patches" << std::endl; + std::cout << "export OSXCROSS_TARGET_DIR=" << target.execpath << "/.." + << std::endl; + std::cout << "export OSXCROSS_BUILD_DIR=" << target.execpath << "/../../build" + << std::endl; + std::cout << "export OSXCROSS_CCTOOLS_PATH=" << target.execpath << std::endl; + std::cout << "export OSXCROSS_LINKER_VERSION=" << getLinkerVersion() + << std::endl; + + exit(EXIT_SUCCESS); +} + +// +// Program 'osxcross-env' +// + +__attribute__((noreturn)) void prog_osxcross_env(int argc, char **argv) { + char epath[PATH_MAX + 1]; + char *oldpath = getenv("PATH"); + char *oldlibpath = getenv("LD_LIBRARY_PATH"); + constexpr const char *ltopath = getLibLTOPath(); + + assert(oldpath); + + if (!getExecutablePath(epath, sizeof(epath))) { + exit(EXIT_FAILURE); + } + + // TODO: escape? + + auto check = [](const char * p, const char * desc)->const char * { + if (!p) + return nullptr; + + const char *pp = p; + + for (; *p; ++p) { + auto badChar = [&](const char *p) { + std::cerr << desc << " should not contain '" << *p << "'" << std::endl; + + const char *start = + p - std::min(static_cast(p - pp), static_cast(30)); + + size_t len = std::min(strlen(start), static_cast(60)); + std::cerr << std::string(start, len) << std::endl; + + while (start++ != p) { + std::cerr << " "; + } + std::cerr << "^" << std::endl; + + exit(EXIT_FAILURE); + }; + switch (*p) { + case '"': + case '\'': + case '$': + case ' ': + case ';': + badChar(p); + } + } + return pp; + }; + + if (argc <= 1) { + const std::string &pname = getParentProcessName(); + + if (pname == "csh" || pname == "tcsh") { + std::cerr << std::endl << "you are invoking this program from a C shell, " + << std::endl << "please use " << std::endl << std::endl + << "setenv PATH `" << epath << "/osxcross-env -v=PATH`" + << std::endl << "setenv LD_LIBRARY_PATH `" << epath + << "/osxcross-env -v=LD_LIBRARY_PATH`" << std::endl << std::endl + << "instead." << std::endl << std::endl; + } + } + + auto hasPath = [](const char * ov, const char * v, const char * vs)->bool { + // ov = old value + // v = value + // vs = value suffix + + if (!ov || !v) + return false; + + bool hasPathSeparator = false; + + for (auto p = ov; *p; ++p) { + if (*p == ':') { + hasPathSeparator = true; + break; + } + } + + static std::string tmp; + + auto check = [&](int t)->bool { + tmp.clear(); + + if (t == 0) + tmp = ':'; + + tmp += v; + + if (vs) { + tmp += vs; + } + + if (t == 1) { + tmp += ':'; + } + + return strstr(ov, tmp.c_str()) != nullptr; + }; + + return ((hasPathSeparator && (check(0) || check(1))) || check(-1)); + }; + + check(oldpath, "PATH"); + check(oldlibpath, "LD_LIBRARY_PATH"); + check(ltopath, "LIB LTO PATH"); + + std::stringstream path; + std::stringstream librarypath; + std::map vars; + + path << oldpath; + + if (!hasPath(oldpath, epath, nullptr)) { + path << ":" << epath; + } + + if (oldlibpath) { + librarypath << oldlibpath; + } + + if (!hasPath(oldlibpath, epath, "/../lib")) { + librarypath << ":" << epath << "/../lib"; + } + + if (ltopath && !hasPath(oldlibpath, ltopath, nullptr)) { + librarypath << ":" << ltopath; + } + + vars["PATH"] = path.str(); + vars["LD_LIBRARY_PATH"] = librarypath.str(); + + auto printVariable = [&](const std::string &var) { + auto it = vars.find(var); + if (it == vars.end()) { + std::cerr << "unknown variable '" << var << "'" << std::endl; + exit(EXIT_FAILURE); + } + std::cout << it->second << std::endl; + }; + + if (argc <= 1) { + std::cout << std::endl; + for (auto &v : vars) { + std::cout << "export " << v.first << "="; + printVariable(v.first); + std::cout << std::endl; + } + } else { + if (strncmp(argv[1], "-v=", 3)) { + exit(EXIT_FAILURE); + } + const char *var = argv[1] + 3; + printVariable(var); + } + + exit(EXIT_SUCCESS); +} + +// +// Program 'dsymutil' +// + +__attribute__((noreturn)) void prog_dsymutil(int argc, char **argv) { + (void)argc; + (void)argv; + + exit(EXIT_SUCCESS); +} + +// +// detectTarget(): +// - detect target and setup invocation command +// +// This function also handles/implements helper programs +// like 'sw_vers', 'osxcross-env' and 'osxcross-conf' +// + +bool detectTarget(int argc, char **argv, Target &target) { + const char *cmd = argv[0]; + const char *p = strrchr(cmd, '/'); + size_t i = 0; + + if (p) + cmd = &p[1]; + + target.args.reserve(static_cast(argc)); + + auto warnExtension = [](const char *extension) { + std::cerr << "warning: '" << extension << "' is an OSXCross extension" + << std::endl; + }; + + auto parseArgs = [&]() { + + auto getVal = [&](char * arg, const char * flag, int & i)->const char * { + const char *val = arg + strlen(flag); + + if (!*val) { + val = argv[++i]; + + if (i >= argc) { + std::cerr << "missing argument for '" << val << "'" << std::endl; + return nullptr; + } + } + + return val; + }; + + for (int i = 1; i < argc; ++i) { + char *arg = argv[i]; + + if (!strncmp(arg, "-mmacosx-version-min=", 21)) { + const char *val = arg + 21; + target.OSNum = parseOSVersion(val); + + if (target.OSNum != val) { + std::cerr << "warning: '-mmacosx-version-min=' (" + << target.OSNum.Str() << " != " << val << ")" << std::endl; + } + } else if (!strncmp(arg, "-stdlib=", 8)) { + const char *val = arg + 8; + + if (!strcmp(val, "libc++")) { + target.stdlib = StdLib::libcxx; + } else if (!strcmp(val, "libstdc++")) { + target.stdlib = StdLib::libstdcxx; + } + + if (target.isGCC()) { + warnExtension("-stdlib="); + } + } else if (!strncmp(arg, "-std=", 5)) { + const char *val = arg + 5; + target.langstd = val; + } else if (!strcmp(arg, "-oc-use-gcc-libs")) { + if (target.isGCC()) { + std::cerr << "warning: '" << arg << "' has no effect" << std::endl; + continue; + } + target.usegcclibs = true; + } else if (!strncmp(arg, "-o", 2)) { + target.outputname = getVal(arg, "-o", i); + } else if (!strncmp(arg, "-x", 2)) { + target.lang = getVal(arg, "-x", i); + } else if (!strcmp(arg, "-m32")) { + target.addMachine(Machine::i386); + } else if (!strcmp(arg, "-m64")) { + target.addMachine(Machine::x86_64); + } else if (!strncmp(arg, "-arch", 5)) { + const char *val = getVal(arg, "-arch", i); + + if (!val) + return; + + Machine machine = parseMachine(val); + + if (machine == Machine::unknown) { + std::cerr << "warning '-arch': unknown machine type '" << val << "'" + << std::endl; + } + + const char *name = getMachineString(machine); + + if (strcmp(val, name)) { + std::cerr << "warning '-arch': " << val << " != " << name + << std::endl; + } + + target.addMachine(machine); + } else { + if (arg[0] != '-') { + // Detect source file + + const char *prevarg = ""; + + if (i > 1) { + prevarg = argv[i - 1]; + + if (prevarg[0] == '-' && strlen(prevarg) > 2) { + prevarg = ""; + } + } + + if (prevarg[0] != '-' || !strcmp(prevarg, "-c")) { + const char *ext = strrchr(arg, '.'); + + if (!ext || (strcmp(ext, ".o") && strcmp(ext, ".a"))) + target.sourcefile = arg; + } + } + + target.args.push_back(arg); + } + } + }; + + auto checkCXXLib = [&]() { + if (target.compiler.rfind("-libc++") == (target.compiler.size() - 7)) { + if (target.stdlib != StdLib::unset && target.stdlib != StdLib::libcxx) { + std::cerr << "warning: '-stdlib=" << getStdLibString(target.stdlib) + << "' will be ignored" << std::endl; + } + + target.compiler.resize(target.compiler.size() - 7); + target.stdlib = StdLib::libcxx; + } + }; + + if (!strncmp(cmd, "osxcross-conf", 13)) { + prog_osxcross_conf(target); + } else if (!strncmp(cmd, "osxcross-env", 12)) { + prog_osxcross_env(argc, argv); + } else if (!strncmp(cmd, "sw_vers", 7)) { + prog_sw_vers(argc, argv); + } else if (!strncmp(cmd, "dsymutil", 8)) { + prog_dsymutil(argc, argv); + } + + for (auto M : MachineStrs) { + const size_t len = strlen(M); + ++i; + + if (!strncmp(cmd, M, len)) { + target.machine = static_cast(i - 1); + cmd += len; + + if (*cmd++ != '-') + return false; + + if (strncmp(cmd, "apple-", 6)) + return false; + + cmd += 6; + + if (strncmp(cmd, "darwin", 6)) + return false; + + if (!(p = strchr(cmd, '-'))) + return false; + + target.target = std::string(cmd, p - cmd); + target.compiler = &p[1]; + + if (target.compiler == "wrapper") { + exit(EXIT_SUCCESS); + } else if (target.compiler == "sw_vers") { + prog_sw_vers(argc, argv); + } else if (target.compiler == "dsymutil") { + prog_dsymutil(argc, argv); + } else if (target.compiler == "cc") { + target.compiler = getDefaultCompiler(); + } else if (target.compiler == "c++") { + target.compiler = getDefaultCXXCompiler(); + } + + if (target.target != getDefaultTarget()) { + std::cerr << "warning: target mismatch (" << target.target + << " != " << getDefaultTarget() << ")" << std::endl; + } + + parseArgs(); + checkCXXLib(); + return target.Setup(); + } + } + + if (!strncmp(cmd, "o32", 3)) { + target.machine = Machine::i386; + } else if (!strncmp(cmd, "o64", 3)) { + target.machine = Machine::x86_64; + } else { + return false; + } + + if (cmd[3]) + target.compiler = &cmd[4]; + + parseArgs(); + checkCXXLib(); + return target.Setup(); +} + +} // unnamed namespace + +// +// Main routine +// + +int main(int argc, char **argv) { + char bbuf[sizeof(benchmark)]; + auto b = new (bbuf) benchmark; + Target target; + bool debug = false; + + if (!detectTarget(argc, argv, target)) { + std::cerr << "cannot detect target" << std::endl; + return 1; + } + + if (char *p = getenv("OCDEBUG")) { + debug = (p[0] == '1'); + } + + if (debug) { + b->halt(); + std::cerr << "detected target triple: " << target.getTriple() << std::endl; + std::cerr << "detected compiler: " << target.compiler << std::endl; + + std::cerr << "detected stdlib: " << getStdLibString(target.stdlib) + << std::endl; + + // std::cerr << "detected source file: " + // << (target.sourcefile ? target.sourcefile : "") << std::endl; + + std::cerr << "detected language: " << target.getLangName() << std::endl; + b->resume(); + } + + auto cargs = new char *[target.fargs.size() + target.args.size() + 1]; + size_t i = 0; + + for (auto &arg : target.fargs) { + cargs[i++] = const_cast(arg.c_str()); + } + + for (auto &arg : target.args) { + cargs[i++] = const_cast(arg.c_str()); + } + + cargs[i] = nullptr; + + auto printCommand = [&]() { + std::string in; + std::string out; + + for (int i = 0; i < argc; ++i) { + in += argv[i]; + in += " "; + } + + for (auto &arg : target.fargs) { + out += arg; + out += " "; + } + + for (auto &arg : target.args) { + out += arg; + out += " "; + } + + std::cerr << "command (in): " << in << std::endl; + std::cerr << "command (out): " << out << std::endl; + }; + + concatEnvVariable("COMPILER_PATH", target.execpath); + + if (debug) { + time_type diff = b->getDiff(); + printCommand(); + std::cerr << "time spent in wrapper: " << diff / 1000000.0 << " ms" + << std::endl; + } + + if (execvp(cargs[0], cargs)) { + std::cerr << "invoking compiler failed" << std::endl; + + if (!debug) + printCommand(); + + return 1; + } + + __builtin_unreachable(); +} diff --git a/wrapper/oscompat.h b/wrapper/oscompat.h new file mode 100644 index 0000000..154da43 --- /dev/null +++ b/wrapper/oscompat.h @@ -0,0 +1,14 @@ +namespace { +#ifdef _WIN32 +int setenv(const char *name, const char *value, int overwrite) { + std::string buf; + (void)overwrite; // TODO + + buf = name; + buf += '='; + buf += value; + + return putenv(buf.c_str()); +} +#endif +}