Last active
October 23, 2017 07:50
-
-
Save Tehnix/dd8a71322744bb92ef3176ebc6f0b7f9 to your computer and use it in GitHub Desktop.
Script to easily manage hackage.mobilehaskell.org, setup with: curl -o /usr/local/bin/cross-ghc https://gist.githubusercontent.com/Tehnix/dd8a71322744bb92ef3176ebc6f0b7f9/raw/db0977544c66cfa3fed7ceea652f3b43c1c1c1d9/cross-ghc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
defaultToolchainWrapperPath="/usr/local/lib/toolchain-wrapper" | |
defaultLlvmPath="/usr/local/opt/llvm/bin" | |
defaultLibffiPath="/usr/local/opt/libffi/lib" | |
crossGhcDistributionPath="/usr/local/lib/cross-ghc" | |
# Distribution specific information. | |
ghcVersion="ghc-8.3.20171020" | |
commitHash="aa5f532d20" | |
validTargetList="{ios-sim[ulator]|ios|android|android-32|raspberry|macos}" | |
# Print command line help information. | |
function helpInformation() { | |
echo "Usage: cross-ghc <command> [target]" | |
echo "" | |
echo "For building to a target:" | |
echo " cross-ghc $validTargetList [ghc-options] <file>" | |
echo " cross-ghc static $validTargetList [ghc-options] <file>" | |
echo "" | |
echo "the last one adds the -staticlib flag, and other things, to produce" | |
echo "a static archive in 'hs-libs/<architecture>/libhs.a'." | |
echo "" | |
echo "Specifically for building a universal library for iOS:" | |
echo " cross-ghc universal [ghc-options] <file>" | |
echo "" | |
echo "Which builds cross-ghc static ios|ios-sim and then combines the libraries with" | |
echo "libo after resulting in a universal library at hs-libs/libhs.a." | |
echo "" | |
echo "For compiling the ghc binaries:" | |
echo " cross-ghc build-ghc $validTargetList" | |
echo "" | |
echo "Finally, for managing the toolchain-wrapper:" | |
echo " cross-ghc get-toolchain" | |
echo " cross-ghc update-toolchain" | |
echo "" | |
echo "By default the toolchain-wrapper path is set to '/usr/local/lib/toolchain-wrapper'," | |
echo "you can override this by setting \$ghcCrossToolchainPath, and similarly LLVM5," | |
echo "which is set to '/usr/local/opt/llvm/bin' can be overridden by setting" | |
echo "\$ghcCrossLLVMPath." | |
echo "" | |
echo "Note that it is assumed that LLVM5 is already installed. If not, you can install" | |
echo "it on macOS via 'brew install llvm'. On other systems, consult you package manager." | |
echo "" | |
echo "cross-ghc also assumes libffi is set up. If not, you can install it on macOS via" | |
echo "'brew install libffi'. On other systems, consult your package manager." | |
echo "" | |
} | |
# Set the host configure parameter. | |
function ensureHostIsSet() { | |
# Get the platform information. | |
platform='unknown' | |
unameStr=`uname` | |
if [[ "$unameStr" == 'Linux' ]]; then | |
platform='unknown-linux' | |
elif [[ "$unameStr" == 'Darwin' ]]; then | |
platform='apple-darwin' | |
fi | |
# Get the architecture information. | |
machineType='unknown' | |
machineStr=`uname -m` | |
if [[ "$machineStr" == 'x86_64' ]]; then | |
machineType='x86_64' | |
elif [[ "$machineStr" == 'arm' ]]; then | |
machineType='arm' | |
fi | |
hostConfigure="$machineType-$platform" | |
} | |
# Set the path to the toolchain-wrapper. | |
function ensureToolChainPath() { | |
if [ -n "$ghcCrossToolchainPath" ]; then | |
toolchainWrapperPath="$ghcCrossToolchainPath" | |
if [ ! -d "$toolchainWrapperPath" ]; then | |
>&2 echo "Nothing found at $toolchainWrapperPath, which was specificed as the path to the toolchain-wrapper! Please make sure that the toolchain is setup, e.g. by running 'cross-ghc get-toolchain'." | |
exit 1 | |
fi | |
elif [ -d "$defaultToolchainWrapperPath" ]; then | |
toolchainWrapperPath=$defaultToolchainWrapperPath | |
else | |
>&2 echo "Could not find toolchain-wrapper at $defaultToolchainWrapperPath, and an alternative path was not defined in \$ghcCrossToolchainPath." | |
exit 1 | |
fi | |
} | |
# Set the path to the LLVM5. | |
function ensureLlvmPath() { | |
if [ -n "$ghcCrossLLVMPath" ]; then | |
llvmPath="$ghcCrossLLVMPath" | |
if [ ! -d "$llvmPath" ]; then | |
>&2 echo "Nothing found at $llvmPath, which was specificed as the path to LLVM5! Please make sure LLVM is installed." | |
exit 1 | |
fi | |
elif [ -d "$defaultLlvmPath" ]; then | |
llvmPath=$defaultLlvmPath | |
else | |
>&2 echo "Could not find LLVM5 at $defaultLlvmPath, and an alternative path was not defined in \$ghcCrossLlvmPath." | |
exit 1 | |
fi | |
} | |
# Set the path to the LLVM5. | |
function ensureLibffiPath() { | |
if [ -n "$ghcCrossLibffiPath" ]; then | |
libffiPath="$ghcCrossLibffiPath" | |
if [ ! -d "$llvmPath" ]; then | |
>&2 echo "Nothing found at $libffiPath, which was specificed as the path to libffi! Please make sure libffi is installed." | |
exit 1 | |
fi | |
elif [ -d "$defaultLibffiPath" ]; then | |
libffiPath=$defaultLibffiPath | |
else | |
>&2 echo "Could not find libffi at $defaultLibffiPath, and an alternative path was not defined in \$ghcCrossLibffiPath." | |
exit 1 | |
fi | |
} | |
function ensureGhcOnPath() { | |
if [ ! $(which $targetPrefix-ghc) ]; then | |
>&2 echo "Could not find $targetPrefix-ghc on path! Are you sure you have" | |
>&2 echo "built the GHC binaries for this distribution? See 'cross-ghc help'" | |
>&2 echo "for more information." | |
>&2 echo "" | |
exit 1 | |
fi | |
} | |
# Build GHC binaries for a specific target. | |
function buildGhcBinaries() { | |
echo "Starting process to build the GHC for the target platform." | |
echo "" | |
# Make sure the distribution exists and then configure/build it. | |
getBinaryDistruibution \ | |
&& (cd $crossGhcDistributionPath/ghc-$targetPrefix && buildAndConfigureDistribution) | |
} | |
function getBinaryDistruibution() { | |
if [ ! -d "$crossGhcDistributionPath/ghc-$targetPrefix" ]; then | |
# Download the distribution, unzip and untar it, and then finally configure | |
# and build the GHC binaries. | |
echo "Choosing distribution: $ghcDistribution" | |
echo "" | |
mkdir -p $crossGhcDistributionPath \ | |
&& curl -o $crossGhcDistributionPath/ghc-$targetPrefix.tar.xz \ | |
-O -L -C - $ghcDistribution \ | |
--retry 20 \ | |
&& gunzip -c $crossGhcDistributionPath/ghc-$targetPrefix.tar.xz > $crossGhcDistributionPath/ghc-$targetPrefix.tar \ | |
&& tar xf $crossGhcDistributionPath/ghc-$targetPrefix.tar -C $crossGhcDistributionPath \ | |
&& mv $crossGhcDistributionPath/$ghcVersion $crossGhcDistributionPath/ghc-$targetPrefix \ | |
&& echo "Downloaded and unpacked distribution to $crossGhcDistributionPath/ghc-$targetPrefix" \ | |
&& echo "" | |
else | |
echo "Using existing GHC distribution for $target." | |
echo "" | |
fi | |
} | |
function buildAndConfigureDistribution() { | |
export PATH="$toolchainWrapperPath:$llvmPath:$PATH" | |
./configure \ | |
--prefix=$crossGhcDistributionPath/ghc-$targetPrefix \ | |
--target=$targetPrefix \ | |
--host=$hostConfigure \ | |
--build=$hostConfigure \ | |
&& make install \ | |
&& echo "" \ | |
&& echo "Finished building $targetPrefix-ghc." \ | |
&& echo "" | |
} | |
function getToolchain() { | |
# We manually set the path here, because `ensureToolChainPath` requires | |
# that the path exists in advance. | |
toolchainWrapperPath=$defaultToolchainWrapperPath | |
if [ -n "$ghcCrossToolchainPath" ]; then | |
toolchainWrapperPath="$ghcCrossToolchainPath" | |
fi | |
echo "Cloning down the toolchain-wrapper into $toolchainWrapperPath." | |
echo "" | |
git clone https://github.com/zw3rk/toolchain-wrapper.git $toolchainWrapperPath | |
echo "" | |
echo "Running ./bootstrap" | |
(cd $toolchainWrapperPath && exec $toolchainWrapperPath/bootstrap) | |
echo "" | |
echo "Finished setting up toolchain" | |
echo "" | |
} | |
function updateToolchain() { | |
echo "Updating toolchain-wrapper." | |
echo "" | |
git -C $toolchainWrapperPath pull origin master | |
echo "" | |
echo "Re-running ./bootstrap" | |
(cd $toolchainWrapperPath && exec $toolchainWrapperPath/bootstrap) | |
echo "" | |
echo "Finished setting up toolchain" | |
echo "" | |
} | |
# Translate the target into its target string. | |
function getTarget() { | |
target=$1 | |
case "$target" in | |
ios-simulator|ios-sim) | |
targetPrefix="x86_64-apple-ios" | |
targetBuildArch="x86_64" | |
;; | |
ios) | |
targetPrefix="aarch64-apple-ios" | |
targetBuildArch="arm64" | |
;; | |
macos) | |
targetPrefix="x86_64-apple-darwin" | |
targetBuildArch="x86_64" | |
;; | |
android) | |
targetPrefix="aarch64-unknown-linux-android" | |
targetBuildArch="arm64" | |
;; | |
android-32) | |
targetPrefix="arm-unknown-linux-android" | |
targetBuildArch="arm64" | |
;; | |
raspberry) | |
targetPrefix="arm-unknown-linux" | |
targetBuildArch="arm64" | |
;; | |
*) | |
>&2 echo "Invalid target!" | |
>&2 echo "" | |
echo "Valid targets are:" | |
echo " $validTargetList" | |
echo "" | |
echo "See 'cross-ghc help' for more information." | |
echo "" | |
exit 1 | |
esac | |
ghcDistribution="http://hackage.mobilehaskell.org/$hostConfigure/$commitHash/$ghcVersion-$targetPrefix.tar.xz" | |
} | |
# Handle command line arguments. | |
case "$1" in | |
ios-simulator|ios-sim|ios|android|android-32|raspberry|macos) | |
ensureHostIsSet | |
getTarget $1 | |
ensureToolChainPath | |
ensureLlvmPath | |
export PATH="$toolchainWrapperPath:$llvmPath:/usr/local/lib/cross-ghc/ghc-$targetPrefix/bin:$PATH" | |
ensureGhcOnPath | |
$targetPrefix-ghc ${@:2} | |
;; | |
static) | |
ensureHostIsSet | |
getTarget $2 | |
ensureToolChainPath | |
ensureLlvmPath | |
ensureLibffiPath | |
export PATH="$toolchainWrapperPath:$llvmPath:/usr/local/lib/cross-ghc/ghc-$targetPrefix/bin:$PATH" | |
ensureGhcOnPath | |
mkdir -p hs-libs/$targetBuildArch | |
mkdir -p hs-libs/build-artifacts/$targetBuildArch | |
$targetPrefix-ghc -lffi -L$libffiPath \ | |
-staticlib \ | |
-odir hs-libs/build-artifacts/$targetBuildArch \ | |
-hidir hs-libs/build-artifacts/$targetBuildArch \ | |
-o hs-libs/$targetBuildArch/libhs.a \ | |
${@:3} | |
;; | |
universal) | |
cross-ghc static ios ${@:2} \ | |
&& cross-ghc static ios-sim ${@:2} \ | |
&& lipo -create -output hs-libs/libhs.a \ | |
hs-libs/arm64/libhs.a hs-libs/x86_64/libhs.a \ | |
&& echo "Created universal library in hs-libs/libhs.a." && echo "" | |
;; | |
build-ghc) | |
ensureHostIsSet | |
getTarget $2 | |
ensureToolChainPath | |
ensureLlvmPath | |
ensureHostIsSet | |
buildGhcBinaries | |
;; | |
get-toolchain) | |
getToolchain | |
;; | |
update-toolchain) | |
ensureToolChainPath | |
updateToolchain | |
;; | |
-h|--help|help) | |
helpInformation | |
;; | |
*) | |
>&2 echo "Invalid command!" | |
>&2 echo "" | |
helpInformation | |
exit 1 | |
esac |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment