diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4cc705a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,72 @@ +# Copyright Lewis Baker 2017 +# +# Distributed under MIT license. +# See LICENSE.txt file for details. + +language: cpp + +os: linux + +dist: trusty + +git: + submodules: true + +addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty + packages: + - python2.7 + - clang-6.0 + - lld-6.0 + - ninja-build + +cache: + directories: + - ${TOOLS_DIR}/cmake-${CMAKE_VERSION} + +compiler: +- clang + +env: + global: + - TOOLS_DIR=${TRAVIS_BUILD_DIR}/tools + - LLVM_INSTALL_PREFIX=${TOOLS_DIR}/llvm + - CMAKE_VERSION="3.9.1" +matrix: + include: + - env: RELEASE=debug ARCH=x64 CLANG_VERSION=6.0 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=6.0 + allow_failures: + # Clang 6.0~svn320382 has a bug that causes optimised builds to fail. + # See https://bugs.llvm.org/show_bug.cgi?id=34897 + - env: RELEASE=optimised ARCH=x64 CLANG_VERSION=6.0 + +before_install: +- export CC="$CC-$CLANG_VERSION" +- export CXX="$CXX-$CLANG_VERSION" +- | + if [ ! -f "${TOOLS_DIR}/cmake-${CMAKE_VERSION}/cached" ]; then + CMAKE_URL="https://cmake.org/files/v3.9/cmake-${CMAKE_VERSION}-Linux-x86_64.tar.gz" + mkdir -p "${TOOLS_DIR}/cmake-${CMAKE_VERSION}" + travis_retry wget --no-check-certificate --quiet -O - "${CMAKE_URL}" | tar --strip-components=1 -xz -C "${TOOLS_DIR}/cmake-${CMAKE_VERSION}" + touch "${TOOLS_DIR}/cmake-${CMAKE_VERSION}/cached" + else + echo "Using cached CMake version ${CMAKE_VERSION}" + fi + export PATH="${TOOLS_DIR}/cmake-${CMAKE_VERSION}/bin:$PATH" + +install: +- ./build-libcxx.sh +- export LD_LIBRARY_PATH="${LLVM_INSTALL_PREFIX}/lib" + +before_script: +- source ./init.sh + +script: +- cake architecture=$ARCH release=$RELEASE --clang-executable="$CC" --libcxx-install-prefix="${LLVM_INSTALL_PREFIX}" + +notifications: + email: false diff --git a/README.md b/README.md index 23c14b6..35734df 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ It has been open-sourced in the hope that others will find it useful and that th can provide feedback on it and ways to improve it. It requires a compiler that supports the coroutines TS: -- Windows + Visual Studio 2017 ![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/lewissbaker/cppcoro?branch=master&svg=true&passingText=master%20-%20OK&failingText=master%20-%20Failing&pendingText=master%20-%20Pending) -- Linux + Clang 5.0/6.0 + libc++ +- Windows + Visual Studio 2017 [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/lewissbaker/cppcoro?branch=master&svg=true&passingText=master%20-%20OK&failingText=master%20-%20Failing&pendingText=master%20-%20Pending)](https://ci.appveyor.com/project/lewissbaker/cppcoro/branch/master) +- Linux + Clang 5.0/6.0 + libc++ [![Build Status](https://travis-ci.org/lewissbaker/cppcoro.svg?branch=master)](https://travis-ci.org/lewissbaker/cppcoro) The Linux version is functional except for the `io_context` and file I/O related classes which have not yet been implemented for Linux (see issue [#15](https://github.com/lewissbaker/cppcoro/issues/15) for more info). diff --git a/args.cake b/args.cake index bf69b11..52b27eb 100644 --- a/args.cake +++ b/args.cake @@ -4,6 +4,7 @@ # This file defines extra command-line args specific to the cppcoro project. ############################################################################## from cake.script import Script +import cake.system parser = Script.getCurrent().engine.parser @@ -16,3 +17,30 @@ parser.add_option( help="Create projects instead of building a variant.", default=False, ) + +if cake.system.isLinux() or cake.system.isDarwin(): + parser.add_option( + "--clang-install-prefix", + dest="clangInstallPrefix", + type="string", + metavar="PATH", + default=None, + help="Path where clang has been installed." + ) + + parser.add_option( + "--clang-executable", + dest="clangExecutable", + type="string", + metavar="FILE", + default="clang", + help="Name or full-path of clang executable to compile with") + + parser.add_option( + "--libcxx-install-prefix", + dest="libcxxInstallPrefix", + type="string", + metavar="PATH", + default=None, + help="Path where libc++ has been installed.\n" + "Defaults to value of --clang-install-prefix") diff --git a/build-clang.sh b/build-clang.sh new file mode 100755 index 0000000..e652216 --- /dev/null +++ b/build-clang.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# +# Builds Clang and LLD from source. + +export WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export LLVM_PROJECT="${WORKSPACE_ROOT}/tools/llvm-project" + +: "${LLVM_REPO:=https://github.com/llvm-mirror/llvm.git}" +: "${LLVM_REF:=master}" + +: "${CLANG_REPO:=https://github.com/llvm-mirror/clang.git}" +: "${CLANG_REF:=master}" + +: "${LLD_REPO:=https://github.com/llvm-mirror/lld.git}" +: "${LLD_REF:=master}" + +: "${LIBCXX_REPO:=https://github.com/llvm-mirror/libcxx.git}" +: "${LIBCXX_REF:=master}" + +: "${LLVM_INSTALL_PREFIX:=$WORKSPACE_ROOT/build/llvm-install}" + +: "${CXX:=clang++}" +: "${CC:=clang}" + +if [ ! -d "${LLVM_PROJECT}" ]; then + mkdir -p "${LLVM_PROJECT}" || exit +fi + +cd "${LLVM_PROJECT}" || exit + +if [ ! -d llvm/.git ]; then + git clone --depth=1 -b "$LLVM_REF" -- "$LLVM_REPO" llvm || exit +else + (cd llvm && + git remote set-url origin "$LLVM_REPO" && + git fetch origin "$LLVM_REF" && + git checkout FETCH_HEAD) || exit +fi + +if [ ! -d llvm/tools/clang/.git ]; then + git clone --depth=1 -b "$CLANG_REF" -- "$CLANG_REPO" llvm/tools/clang || exit + ln -s llvm/tools/clang clang || exit +else + (cd llvm/tools/clang && + git remote set-url origin "$CLANG_REPO" && + git fetch --depth=1 origin "$CLANG_REF" && + git checkout FETCH_HEAD) || exit +fi + +if [ ! -d llvm/tools/lld/.git ]; then + git clone --depth=1 -b "$LLD_REF" -- "$LLD_REPO" llvm/tools/lld || exit + ln -s llvm/tools/lld lld || exit +else + (cd llvm/tools/lld && + git remote set-url origin "$LLD_REPO" && + git fetch --depth=1 origin "$LLD_REF" && + git checkout FETCH_HEAD) || exit +fi + +if [ ! -d llvm/projects/libcxx.git ]; then + git clone --depth=1 -b "$LIBCXX_REF" -- "$LIBCXX_REPO" llvm/projects/libcxx || exit + ln -s llvm/projects/libcxx libcxx || exit +else + (cd llvm/projects/libcxx && + git remote set-url origin "$LIBCXX_REPO" && + git fetch --depth=1 origin "$LIBCXX_REF" && + git checkout FETCH_HEAD) || exit +fi + +cd "$WORKSPACE_ROOT" || exit + +if [ ! -d build/clang ]; then + mkdir -p build/clang || exit +fi + +# Build clang toolchain +(mkdir -p build/clang && \ + cd build/clang && \ + cmake -GNinja \ + -DCMAKE_CXX_COMPILER="$CXX" \ + -DCMAKE_C_COMPILER="$CC" \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \ + -DCMAKE_BUILD_WITH_INSTALL_RPATH="yes" \ + -DLLVM_TARGETS_TO_BUILD=X86 \ + -DLLVM_ENABLE_PROJECTS="lld;clang" \ + "${LLVM_PROJECT}/llvm" && \ + ninja install-clang \ + install-clang-headers \ + install-llvm-ar \ + install-lld) || exit + +# Build libcxx using clang we just built +(mkdir -p build/libcxx && \ + cd build/libcxx && \ + cmake -GNinja \ + -DCMAKE_CXX_COMPILER="${LLVM_INSTALL_PREFIX}/bin/clang++" \ + -DCMAKE_C_COMPILER="${LLVM_INSTALL_PREFIX}/bin/clang" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \ + -DLLVM_PATH="${LLVM_PROJECT}/llvm" \ + -DLIBCXX_CXX_ABI=libstdc++ \ + "${LLVM_PROJECT}/libcxx" && \ + ninja install) || exit diff --git a/build-libcxx.sh b/build-libcxx.sh new file mode 100755 index 0000000..08e7f81 --- /dev/null +++ b/build-libcxx.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# +# Builds libc++ from source + +export WORKSPACE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export LLVM_PROJECT="${WORKSPACE_ROOT}/tools/llvm-project" + +: "${LLVM_REPO:=https://github.com/llvm-mirror/llvm.git}" +: "${LLVM_REF:=master}" + +: "${LIBCXX_REPO:=https://github.com/llvm-mirror/libcxx.git}" +: "${LIBCXX_REF:=master}" + +: "${LLVM_INSTALL_PREFIX:=$WORKSPACE_ROOT/build/llvm-install}" + +: "${CXX:=clang++}" +: "${CC:=clang}" + +if [ ! -d "${LLVM_PROJECT}" ]; then + mkdir -p "${LLVM_PROJECT}" || exit +fi + +cd "${LLVM_PROJECT}" || exit + +if [ ! -d llvm/.git ]; then + git clone --depth=1 -b "$LLVM_REF" -- "$LLVM_REPO" llvm || exit +else + (cd llvm && + git remote set-url origin "$LLVM_REPO" && + git fetch origin "$LLVM_REF" && + git checkout FETCH_HEAD) || exit +fi + +if [ ! -d llvm/projects/libcxx.git ]; then + git clone --depth=1 -b "$LIBCXX_REF" -- "$LIBCXX_REPO" llvm/projects/libcxx || exit + ln -s llvm/projects/libcxx libcxx || exit +else + (cd llvm/projects/libcxx && + git remote set-url origin "$LIBCXX_REPO" && + git fetch --depth=1 origin "$LIBCXX_REF" && + git checkout FETCH_HEAD) || exit +fi + +cd "$WORKSPACE_ROOT" || exit + +(mkdir -p build/libcxx && \ + cd build/libcxx && \ + cmake -GNinja \ + -DCMAKE_CXX_COMPILER="${CXX}" \ + -DCMAKE_C_COMPILER="${CC}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="${LLVM_INSTALL_PREFIX}" \ + -DLLVM_PATH="${LLVM_PROJECT}/llvm" \ + -DLIBCXX_CXX_ABI=libstdc++ \ + "${LLVM_PROJECT}/libcxx" && \ + ninja install) || exit diff --git a/config.cake b/config.cake index 77b7103..969b036 100644 --- a/config.cake +++ b/config.cake @@ -176,45 +176,103 @@ if cake.system.isWindows() or cake.system.isCygwin(): except CompilerNotFoundError, e: print str(e) -elif cake.system.isLinux(): +elif cake.system.isLinux() or cake.system.isDarwin(): + def firstExisting(paths, default=None): + for p in paths: + if os.path.exists(p): + return p + else: + return default + + + + if cake.system.isLinux(): + defaultInstallPaths = ["/usr"] + elif cake.system.isDarwin(): + defaultInstallPaths = ["/usr/local/opt/llvm", "/usr"] + + clangInstallPrefix = engine.options.clangInstallPrefix + clangExe = engine.options.clangExecutable + + if clangInstallPrefix is None: + # If no clang install prefix was specified then try to deduce it + # from the --clang-executable path. + if os.path.isabs(clangExe): + clangBinPath = cake.path.dirName(clangExe) + if cake.path.baseName(clangBinPath) == 'bin': + clangInstallPrefix = cake.path.dirName(clangBinPath) + + # Use sensible system defaults if neither --clang-executable or + # --clang-install-prefix command-line arguments are specified. + if clangInstallPrefix is None: + clangInstallPrefix = firstExisting(defaultInstallPaths, default="/usr") + + libcxxInstallPrefix = engine.options.libcxxInstallPrefix + if libcxxInstallPrefix is None: + libcxxInstallPrefix = clangInstallPrefix + + # If the user didn't specify an absolute path to clang then look for it in + # the installed clang bin directory. + if not os.path.isabs(clangExe): + clangExe = cake.path.join(clangInstallPrefix, 'bin', clangExe) + + # Extract the clang version suffix from the file-name + # We'll try and use other LLVM tools with the same suffix if they exist. + clangName = cake.path.baseName(clangExe) + versionSuffix = clangName[5:] if clangName.startswith('clang-') else "" + + clangExeDir = cake.path.dirName(clangExe) + clangBinPath = cake.path.join(clangInstallPrefix, 'bin') + + binPaths = [clangExeDir] + if clangBinPath not in binPaths: + binPaths.append(clangBinPath) + + llvmArExe = firstExisting( + cake.path.join(binPaths, ['llvm-ar' + versionSuffix, 'llvm-ar', 'ar']), + default="/usr/bin/ar") + + # Prefer lld if available, otherwise fall back to system ld + lldExe = firstExisting( + cake.path.join(binPaths, ['ld.lld' + versionSuffix, 'ld.lld']), + ) + + engine.logger.outputInfo( + "clang: " + clangExe + "\n" + + "llvm-ar: " + llvmArExe + "\n") + from cake.library.compilers.clang import ClangCompiler + if cake.system.isLinux(): + platform = 'linux' + elif cake.system.isDarwin(): + platform = 'darwin' + clangVariant = baseVariant.clone(compiler='clang', - platform='linux', + platform=platform, architecture='x64') - # If you have built your own version of Clang, you can modify - # this variable to point to the CMAKE_INSTALL_PREFIX for - # where you have installed your clang/libcxx build. - clangInstallPrefix = '/usr' - - # Set this to the install-prefix of where libc++ is installed. - # You only need to set this if it is not installed at the same - # location as clangInstallPrefix. - libCxxInstallPrefix = None # '/path/to/install' - - clangBinPath = cake.path.join(clangInstallPrefix, 'bin') - compiler = ClangCompiler( configuration=configuration, - clangExe=cake.path.join(clangBinPath, 'clang'), - llvmArExe=cake.path.join(clangBinPath, 'llvm-ar'), + clangExe=clangExe, + llvmArExe=llvmArExe, binPaths=[clangBinPath]) compiler.addCppFlag('-std=c++1z') compiler.addCppFlag('-fcoroutines-ts') compiler.addCppFlag('-m64') - compiler.addModuleFlag('-fuse-ld=lld') - compiler.addProgramFlag('-fuse-ld=lld') + if lldExe: + compiler.addModuleFlag('-fuse-ld=' + lldExe) + compiler.addProgramFlag('-fuse-ld=' + lldExe) - if libCxxInstallPrefix: + if libcxxInstallPrefix != clangInstallPrefix: compiler.addCppFlag('-nostdinc++') compiler.addIncludePath(cake.path.join( - libCxxInstallPrefix, 'include', 'c++', 'v1')) + libcxxInstallPrefix, 'include', 'c++', 'v1')) compiler.addLibraryPath(cake.path.join( - libCxxInstallPrefix, 'lib')) + libcxxInstallPrefix, 'lib')) else: compiler.addCppFlag('-stdlib=libc++') @@ -229,8 +287,8 @@ elif cake.system.isLinux(): env = clangVariant.tools["env"] env["COMPILER"] = "clang" - env["COMPILER_VERSION"] = "5.0" - env["PLATFORM"] = "linux" + env["COMPILER_VERSION"] = compiler.version + env["PLATFORM"] = platform env["ARCHITECTURE"] = "x64" clangDebugVariant = clangVariant.clone(release='debug') @@ -250,86 +308,11 @@ elif cake.system.isLinux(): compiler = clangOptimisedVariant.tools["compiler"] compiler.addCppFlag('-O3') compiler.addCppFlag('-g') - compiler.addCppFlag('-flto') - compiler.addProgramFlag('-flto') - compiler.addModuleFlag('-flto') - - configuration.addVariant(clangOptimisedVariant) - -elif cake.system.isDarwin(): - - from cake.library.compilers.clang import ClangCompiler - - clangVariant = baseVariant.clone(compiler='clang', - platform='darwin', - architecture='x86_64') - - # If you have built your own version of Clang, you can modify - # this variable to point to the CMAKE_INSTALL_PREFIX for - # where you have installed your clang/libcxx build. - clangInstallPrefix = '/usr/local/opt/llvm' - - # Set this to the install-prefix of where libc++ is installed. - # You only need to set this if it is not installed at the same - # location as clangInstallPrefix. - libCxxInstallPrefix = None # '/path/to/install' - - clangBinPath = cake.path.join(clangInstallPrefix, 'bin') - - compiler = ClangCompiler( - configuration=configuration, - clangExe=cake.path.join(clangBinPath, 'clang'), - llvmArExe=cake.path.join(clangBinPath, 'llvm-ar'), - binPaths=[clangBinPath]) - - compiler.addCppFlag('-std=c++17') - compiler.addCppFlag('-fcoroutines-ts') - compiler.addCppFlag('-m64') - - compiler.addModuleFlag('-fuse-ld=/usr/bin/ld') - compiler.addProgramFlag('-fuse-ld=/usr/bin/ld') - - if libCxxInstallPrefix: - compiler.addCppFlag('-nostdinc++') - compiler.addIncludePath(cake.path.join( - libCxxInstallPrefix, 'include', 'c++', 'v1')) - compiler.addLibraryPath(cake.path.join( - libCxxInstallPrefix, 'lib')) - else: - compiler.addCppFlag('-stdlib=libc++') - - compiler.addLibrary('c++') - - #compiler.addProgramFlag('-Wl,--trace') - #compiler.addProgramFlag('-Wl,-v') - - clangVariant.tools['compiler'] = compiler - - env = clangVariant.tools["env"] - env["COMPILER"] = "clang" - env["COMPILER_VERSION"] = "5.0" - env["PLATFORM"] = "darwin" - env["ARCHITECTURE"] = "x86_64" - - clangDebugVariant = clangVariant.clone(release='debug') - clangDebugVariant.tools["env"]["RELEASE"] = 'debug' - - # Configure debug-specific settings here - compiler = clangDebugVariant.tools["compiler"] - compiler.addCppFlag('-O0') - compiler.addCppFlag('-g') - - configuration.addVariant(clangDebugVariant) - - clangOptimisedVariant = clangVariant.clone(release='optimised') - clangOptimisedVariant.tools["env"]["RELEASE"] = 'optimised' - - # Configure optimised-specific settings here - compiler = clangOptimisedVariant.tools["compiler"] - compiler.addCppFlag('-O3') - compiler.addCppFlag('-g') - compiler.addCppFlag('-flto') - compiler.addProgramFlag('-flto') - compiler.addModuleFlag('-flto') + + # Only use link-time optimisation if we're using LLD + if lldExe: + compiler.addCppFlag('-flto') + compiler.addProgramFlag('-flto') + compiler.addModuleFlag('-flto') configuration.addVariant(clangOptimisedVariant) diff --git a/init.sh b/init.sh old mode 100644 new mode 100755 diff --git a/tools/cake b/tools/cake index 88d8c43..db43f33 160000 --- a/tools/cake +++ b/tools/cake @@ -1 +1 @@ -Subproject commit 88d8c43e9ec2e49adc89e671c849b0b1f5814e4d +Subproject commit db43f336b95d367163b967491f4e22d252ae59b4