diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..6ec789d231 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,231 @@ +version: 2.1 + +orbs: + win: circleci/windows@1.0.0 + +executors: + bionic: + docker: + - image: buildpack-deps:bionic + +jobs: + flake8: + executor: bionic + steps: + - checkout + - run: + name: install pip + command: | + apt-get update -q + apt-get install -q -y python-pip python3-pip + - run: python2 -m pip install --upgrade pip + - run: python3 -m pip install --upgrade pip + - run: python2 -m pip install flake8==3.7.8 + - run: python3 -m pip install flake8==3.7.8 + - run: python2 -m flake8 --show-source --statistics + - run: python3 -m flake8 --show-source --statistics + test-linux: + executor: bionic + environment: + EMSDK_NOTTY: "1" + # I don't know why circleci VMs pretent to have 36 cores but its a lie. + EMSDK_NUM_CORES: "4" + steps: + - checkout + - run: + name: Install debian packages + command: apt-get update -q && apt-get install -q -y cmake build-essential openjdk-8-jre-headless ksh zsh + - run: test/test.sh + - run: test/test_source_env.sh + - run: + name: test.py + command: | + source emsdk_env.sh + test/test.py + test-mac: + macos: + xcode: "12.2.0" + environment: + EMSDK_NOTTY: "1" + # Without this, any `brew installl` command will result in self-update of + # brew itself which takes more than 4 minutes. + HOMEBREW_NO_AUTO_UPDATE: "1" + steps: + - checkout + - run: + name: Install cmake + command: brew install cmake + - run: test/test.sh + - run: + name: test.py + command: | + source emsdk_env.sh + test/test.py + test-windows: + executor: + name: win/vs2019 + shell: bash.exe + environment: + # We need python installed before we can test anytyhing. + # There seems to be undocument copy of python installed here. Hopefully + # if this disappears there will be another way of getting a re-installed + # version. + PYTHON_BIN: "C:\\Python27amd64" + PYTHONUNBUFFERED: "1" + EMSDK_NOTTY: "1" + steps: + - checkout + - run: + name: Add python to bash path + command: echo "export PATH=\"$PATH:/c/python27amd64/\"" >> $BASH_ENV + - run: + name: Install latest + shell: cmd.exe + command: test\test.bat + - run: + name: test.py + command: | + source emsdk_env.sh + python test/test.py + + - run: + name: flagless (process/shell) test + shell: powershell.exe + command: | + test/test_activation.ps1 + + - run: + name: --permanent test + shell: powershell.exe + command: | + $env:PERMANENT_FLAG="--permanent" + test/test_activation.ps1 + + - run: + name: --system test + shell: powershell.exe + command: | + $env:SYSTEM_FLAG="--system" + test/test_activation.ps1 + + - run: + name: Process/Shell PATH preservation test + shell: powershell.exe + command: | + test/test_path_preservation.ps1 + + - run: + name: User PATH preservation test + shell: powershell.exe + command: | + $env:PERMANENT_FLAG="--permanent" + test/test_path_preservation.ps1 + + - run: + name: System PATH preservation test + shell: powershell.exe + command: | + $env:SYSTEM_FLAG="--system" + test/test_path_preservation.ps1 + + build-docker-image: + executor: bionic + steps: + - checkout + - run: + name: install docker + command: apt-get update -q && apt-get install -q -y docker.io + - setup_remote_docker: + version: 19.03.13 + # Build and test the tip-of-tree build of EMSDK + - run: + name: build + command: make -C ./docker version=tot build + - run: + name: test + command: make -C ./docker version=tot test + + publish-docker-image: + executor: bionic + steps: + - checkout + - run: + name: install docker + command: apt-get update -q && apt-get install -q -y docker.io + - setup_remote_docker: + version: 19.03.13 + - run: + name: build + command: make -C ./docker version=${CIRCLE_TAG} build + - run: + name: test + command: make -C ./docker version=${CIRCLE_TAG} test + - run: + name: push image + command: | + docker login -u "$DOCKER_USER" -p "$DOCKER_PASS" + make -C ./docker version=${CIRCLE_TAG} alias=latest push + + test-bazel-linux: + executor: bionic + steps: + - checkout + - run: apt-get install -q -y curl gnupg + - run: curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg + - run: mv bazel.gpg /etc/apt/trusted.gpg.d/ + - run: echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list + - run: + name: install pip + command: | + apt-get update -q + apt-get install -q -y python3-pip + - run: pip3 install absl-py + - run: + name: install bazel + command: | + apt-get install -q -y bazel + - run: test/test_bazel.sh + + test-bazel-mac: + macos: + xcode: "12.2.0" + environment: + EMSDK_NOTTY: "1" + HOMEBREW_NO_AUTO_UPDATE: "1" + steps: + - checkout + - run: brew install grep + - run: + name: install bazel + command: | + brew install bazel + - run: test/test_bazel_mac.sh + +workflows: + flake8: + jobs: + - flake8 + test-linux: + jobs: + - test-linux + test-mac: + jobs: + - test-mac + test-windows: + jobs: + - test-windows + build-docker-image: + jobs: + - build-docker-image + - publish-docker-image: + filters: + branches: + ignore: /.*/ + tags: + only: /.*/ + test-bazel-linux: + jobs: + - test-bazel-linux + test-bazel-mac: + jobs: + - test-bazel-mac diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..dfeb00e532 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +# Ignore all subdirectories +*/* + +# Allow to run the test script inside the Docker container +!/docker/test_dockerimage.sh + +# Ignore unnecessary files inside top-level directory +*.bat +*.csh +*.fish +*.ps1 +*.pyc +.emscripten +.emscripten.old +.emscripten_cache +.emscripten_cache__last_clear +.emscripten_sanity +.emscripten_sanity_wasm +.flake8 +emscripten-releases-tot.txt +README.md diff --git a/.flake8 b/.flake8 index e13176568a..b67d9c2198 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,23 @@ [flake8] -ignore = E111,E114,E501,E261,E266,E121,E402,E241,E701 -filename = emsdk +ignore = + E111, # Indentation is not a multiple of four + E114, # Indentation is not a multiple of four (comment) + E501, # Line too long + E121, # Continuation line under-indented for hanging indent + E722 # bare excepts +exclude = + ./llvm + ./gnu + ./upstream + ./fastcomp + ./fastcomp-clang + ./releases + ./clang + ./emscripten + ./binaryen + ./git + ./node + ./python + ./temp + ./zips + ./crunch diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..1807a00f71 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 365 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 30 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because there has been no + activity in the past year. It will be closed automatically if no further + activity occurs in the next 30 days. Feel free to re-open at any time if this + issue is still relevant. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/.gitignore b/.gitignore index 7ebcf45ce4..229c0b6ba9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,33 @@ -clang -emscripten -emsdk_set_env.bat -emsdk_set_env.sh -git -node -python -temp -zips -crunch -java -mingw -spidermonkey -binaryen -.emscripten -.emscripten_cache -.emscripten_cache__last_clear -.emscripten_sanity -.tmp -tmp -gnu -emscripten-nightlies.txt -llvm-nightlies-32bit.txt -llvm-nightlies-64bit.txt -llvm-tags-32bit.txt -llvm-tags-64bit.txt -upstream +*.pyc +__pycache__ + +# Support for --embedded configs +/.emscripten +/.emscripten.old +/.emscripten_cache +/.emscripten_cache__last_clear +/.emscripten_sanity +/.emscripten_sanity_wasm + +# Tags files that get generated at runtime +/emscripten-releases-tot.txt + +# File that get download/extracted by emsdk itself +/gnu +/upstream +/fastcomp +/fastcomp-clang/ +/llvm +/releases +/clang +/emscripten +/git +/node +/python +/temp +/zips +/crunch +/java +/mingw +/spidermonkey +/binaryen diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b0741a28e5..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: python - -services: - - docker - -before_install: - - docker pull ubuntu:16.04 - -install: - - pip install flake8==3.4.1 - -script: - - flake8 - - set -o errexit - - echo "running..." - - docker build . - -notifications: - email: false diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 03a9fd4630..0000000000 --- a/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -# For travis -FROM buildpack-deps:xenial -SHELL ["/bin/bash", "-c"] -ENV DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 -RUN mkdir -p /root/emsdk/ -COPY . /root/emsdk/ - -RUN cd /root/ \ - && echo "int main() {}" > hello_world.cpp \ - && apt-get update \ - && apt-get install -y python python3 cmake build-essential openjdk-9-jre-headless \ - && /root/emsdk/emsdk update-tags \ - && echo "test latest" \ - && /root/emsdk/emsdk install latest \ - && /root/emsdk/emsdk activate latest \ - && source /root/emsdk/emsdk_env.sh --build=Release \ - && emcc hello_world.cpp \ - && echo "test upstream (waterfall)" \ - && /root/emsdk/emsdk install latest-upstream \ - && /root/emsdk/emsdk activate latest-upstream \ - && source /root/emsdk/emsdk_env.sh --build=Release \ - && emcc hello_world.cpp \ - && python -c "import os ; assert open(os.path.expanduser('~/.emscripten')).read().count('LLVM_ROOT') == 1" \ - && python -c "import os ; assert 'upstream' in open(os.path.expanduser('~/.emscripten')).read()" \ - && echo "test fastcomp (waterfall)" \ - && /root/emsdk/emsdk install latest-fastcomp \ - && /root/emsdk/emsdk activate latest-fastcomp \ - && source /root/emsdk/emsdk_env.sh --build=Release \ - && emcc hello_world.cpp \ - && emcc hello_world.cpp -s WASM=0 \ - && emcc --clear-cache \ - && echo "test latest-releases-upstream" \ - && python2 /root/emsdk/emsdk install latest-releases-upstream \ - && /root/emsdk/emsdk activate latest-releases-upstream \ - && source /root/emsdk/emsdk_env.sh --build=Release \ - && emcc hello_world.cpp \ - && echo "test latest-releases-fastcomp" \ - && python3 /root/emsdk/emsdk install latest-releases-fastcomp \ - && /root/emsdk/emsdk activate latest-releases-fastcomp \ - && source /root/emsdk/emsdk_env.sh --build=Release \ - && emcc hello_world.cpp \ - && echo "test binaryen source build" \ - && /root/emsdk/emsdk install --build=Release binaryen-master-64bit diff --git a/README.md b/README.md index 0f9b03e60c..3d10f87613 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# Emscripten SDK +Emscripten SDK +============== -Emscripten toolchain is distributed as a standalone Emscripten SDK. The SDK +[![CircleCI](https://circleci.com/gh/emscripten-core/emsdk/tree/master.svg?style=svg)](https://circleci.com/gh/emscripten-core/emsdk/tree/master) + +The Emscripten toolchain is distributed as a standalone Emscripten SDK. The SDK provides all the required tools, such as Clang, Python and Node.js along with an update mechanism that enables migrating to newer Emscripten versions as they are released. @@ -8,113 +11,18 @@ released. You can also set up Emscripten from source, without the pre-built SDK, see "Installing from Source" below. -## Downloads +## Downloads / How do I get the latest Emscripten build? To get started with Emscripten development, see the [Emscripten website documentation](https://emscripten.org/docs/getting_started/downloads.html). -**Old Releases** are available in the **Archived Releases** section below. - -## Installation Instructions - -The initial setup process is as follows: - -1. Download and unzip the portable SDK package to a directory of your choice. - This directory will contain the Emscripten SDK. -2. Open a command prompt to the directory of the SDK. -3. Run `emsdk update`. This will fetch the latest registry of available tools. -4. Run `emsdk install latest`. This will download and install the latest set of - precompiled SDK tools. -5. Run `emsdk activate latest`. This will set up **~/.emscripten** to point to - the SDK. -6. Depending on your OS: - - OS X and Linux: Run `source ./emsdk_env.sh`. This will add PATH and other - required environment variables to the currently executing terminal prompt. If - you want to permanently add these environment variables to each terminal - instance on your system, add a call to this command to `.bash_profile` or - another initialization script for your terminal. - - Windows: Call `emsdk_env.bat` to add PATH and other environment variables to - the current command prompt. If you want to persist these environment - variables to all command prompts, run `emsdk activate --global latest` to - have Emsdk edit the Windows registry to add these variables. - -Whenever you change the location of the Portable SDK (e.g. take it to another -computer), re-run steps 5 and 6. If you choose not to permanently add the -environment variables to all terminal prompts, rerun step 6 whenever opening a -new terminal window. - -Note: On Linux and OS X, type `./emsdk` instead of `emsdk` above. - -### Building an Emscripten tag or branch from source - -In addition to providing precompiled compiler versions, Emscripten SDK automates -driving builds of the Emscripten toolchain from source. These builds obtain the -source code directly from GitHub, and can either target a specific tagged -release, or one of the two main Emscripten development branches, `incoming` and -`master`. Building tagged releases from source is useful to build a version of -Emscripten that there is no provided precompiled build available. Building one -of the development branches is useful when you want to get the very latest -version from source, e.g. to verify a brand new bugfix, or to participate to -Emscripten development. - -To build one of the tagged releases from source, run the following steps: - -1. Open a command prompt to the directory of the SDK. -2. Run `emsdk update-tags`. This will ping GitHub to obtain the latest list of - known tags in the different repositories. -3. Run `emsdk list` to check out the list of available tags. Look for the - section "The following SDKs can be compiled from source". -4. Proceed through the steps 4, 5 and 6 above, except substitute `latest` with - the SDK version of your choice, for example `emsdk install - sdk-tag-1.37.9-64bit` followed by `emsdk activate sdk-tag-1.37.9-64bit`. - -To build one of the Github branches, `emsdk install` one of the targets -`sdk-incoming-64bit` or `sdk-master-64bit`. - -Building Emscripten involves building LLVM with Clang from source. LLVM build -configuration allows specifying a number of extra configuration fields, see -here: http://llvm.org/docs/CMake.html. To build the SDK with a specific set of -custom CMake parameters, run the emsdk build script with the environment -variable `LLVM_CMAKE_ARGS="param1=value1,param2=value2,..."`. For example, to -use the gold linker to link the final Clang executable and to enable assertions, -run the installation with the command -`LLVM_CMAKE_ARGS="-DLLVM_USE_LINKER=gold,-DLLVM_ENABLE_ASSERTIONS=ON" ./emsdk -install sdk-incoming-64bit`. - -### Installing emsdk directly from GitHub - -If you want to bootstrap to the development version of emsdk instead of the -stable releases, you can do so by installing emsdk directly from github. -Functionally this behaves identical to the Portable SDK. As a prerequisite on -Windows, you must first manually download and install -[Python](https://www.python.org) to bootstrap, and after that, run: - - git clone https://github.com/juj/emsdk.git - cd emsdk - ./emsdk update-tags - ./emsdk install - ./emsdk activate - source ./emsdk_env.sh (Windows: emsdk_env.bat) - -The only difference in this setup is that you will then use `git pull` instead -of `./emsdk update` to update to a newer version of Emscripten SDK. - -## Getting Started with Emscripten - -The tools in the Emscripten toolchain can be accessed in various ways. Which one -you use depends on your preference. - -##### Command line usage - -The Emscripten compiler is available on the command line by invoking `emcc` or -`em++`. They are located in the folder `emsdk/emscripten//` in the SDK. - -The root directory of the Emscripten SDK contains scripts `emsdk_env.bat` -(Windows) and `emsdk_env.sh` (Linux, OS X) which set up `PATH` and other -environment variables for the current terminal. After calling these scripts, -`emcc`, `clang`, etc. are all accessible from the command line. - -**Check out the tutorial!** See the Emscripten [Tutorial](https://github.com/kripken/emscripten/wiki/Tutorial) page for help on how to get going with the tools from command line. +That explains how to use the emsdk to get the latest binary builds (without +compiling from source). Basically, that amounts to + +``` +./emsdk install latest +./emsdk activate latest +``` ## SDK Concepts @@ -139,15 +47,50 @@ important concepts to help understanding the internals of the SDK: accessed through. Most operations are of the form `emsdk command`. To access the emsdk script, launch the Emscripten Command Prompt. +## System Requirements + +Using the emsdk pre-compiled packages requires only the minimal set of +dependenencies lists below. When building from source a wider set of tools +include git, cmake, and a host compiler are required. See: +https://emscripten.org/docs/building_from_source/toolchain_what_is_needed.html. + +### Mac OS X + +- For Intel-based Macs, macOS 10.13 or newer. For ARM64 M1 based Macs, macOS + 11.0 or newer. +- `java`: For running closure compiler (optional). After installing emscripten + via emsdk, typing 'emcc --help' should pop up a OS X dialog "Java is not + installed. To open java, you need a Java SE 6 runtime. Would you like to + install one now?" that will automatically download a Java runtime to the + system. + +### Linux + +- `python`: Version 2.7.0 or above. +- `java`: For running closure compiler (optional) + +The emsdk pre-compiled binaries are built aginst Ubuntu/Xenial 16.04 LTS and +therefore depend on system libraries compatiable with versions of `glibc` and +`libstdc++` present in that release. If your linux distribution is very old +you may not be able to use the pre-compiled binaries packages. + +### Windows + +- `java`: For running closure compiler (optional) + +## Uninstalling the Emscripten SDK + +To remove the Emscripten SDK, simply delete the emsdk directory. + ## SDK Maintenance The following tasks are common with the Emscripten SDK: -##### How do I work the emsdk utility? +### How do I work the emsdk utility? Run `emsdk help` or just `emsdk` to get information about all available commands. -##### How do I check the installation status and version of the SDK and tools? +### How do I check the installation status and version of the SDK and tools? To get a list of all currently installed tools and SDK versions, and all available tools, run `emsdk list`. @@ -161,38 +104,60 @@ available tools, run `emsdk list`. `source ./emsdk_env.sh` (Linux and OS X) to set up the environment for the calling terminal. -##### How do I install a tool/SDK version? +### How do I install a tool/SDK version? Run the command `emsdk install ` to download and install a new tool or an SDK version. -##### How do I remove a tool or an SDK? +### How do I remove a tool or an SDK? Run the command `emsdk uninstall ` to delete the given tool or SDK from the local hard drive completely. -##### How do I check for updates to the Emscripten SDK? +### How do I check for updates to the Emscripten SDK? -The command `emsdk update` will fetch package information for all new tools and +`emsdk update` will fetch package information for all the new tools and SDK versions. After that, run `emsdk install ` to install a new -version. The command `emsdk update-tags` obtains a list of all new tagged -releases from GitHub without updating Emscripten SDK itself. +version. -##### How do I install an old Emscripten compiler version? +### How do I install an old Emscripten compiler version? Emsdk contains a history of old compiler versions that you can use to maintain your migration path. Type `emsdk list --old` to get a list of archived tool and SDK versions, and `emsdk install ` to install it. -On Windows, you can directly install an old SDK version by using one of the -archived offline NSIS installers. See the **Archived Releases** section down -below. +### I want to build from source! + +Some Emsdk Tool and SDK targets refer to packages that are precompiled, and +no compilation is needed when installing them. Other Emsdk Tools and SDK +targets come "from source", meaning that they will fetch the source repositories +using git, and compile the package on demand. + +When you run `emsdk list`, it will group the Tools and SDKs under these two +categories. + +To obtain and build latest upstream wasm SDK from source, run + +``` +emsdk install sdk-upstream-main-64bit +emsdk activate sdk-upstream-main-64bit +``` -##### When working on git branches compiled from source, how do I update to a newer compiler version? +You can use this target for example to bootstrap developing patches to LLVM, +Binaryen or Emscripten. (After initial installation, use `git remote add` +in the cloned tree to add your own fork to push changes as patches) + +If you only intend to contribute to Emscripten repository, and not to LLVM +or Binaryen, you can also use precompiled versions of them, and only git +clone the Emscripten repository. For more details, see + +https://emscripten.org/docs/contributing/developers_guide.html?highlight=developer#setting-up + +### When working on git branches compiled from source, how do I update to a newer compiler version? Unlike tags and precompiled versions, a few of the SDK packages are based on -"moving" git branches and compiled from source (sdk-incoming, sdk-master, -emscripten-incoming, emscripten-master, binaryen-master). Because of that, the +"moving" git branches and compiled from source (e.g. sdk-upstream-main, +sdk-main, emscripten-main, binaryen-main). Because of that, the compiled versions will eventually go out of date as new commits are introduced to the development branches. To update an old compiled installation of one of this branches, simply reissue the "emsdk install" command on that tool/SDK. This @@ -207,15 +172,14 @@ any odd compilation errors, it is advised to try deleting the intermediate build directory to clear the build (e.g. "emsdk/clang/fastcomp/build_xxx/") before reissuing `emsdk install`. -##### How do I change the currently active SDK version? +### How do I change the currently active SDK version? You can toggle between different tools and SDK versions by running `emsdk activate `. Activating a tool will set up `~/.emscripten` to -point to that particular tool. On Windows, you can pass the option `--global` to -the `activate` command to register the environment permanently to the system -registry for all users. +point to that particular tool. On Windows, you can pass the option `--permanent` to +the `activate` command to register the environment permanently for the current user. Use `--system` to do this for all users. -##### How do I build multiple projects with different SDK versions in parallel? +### How do I build multiple projects with different SDK versions in parallel? By default, Emscripten locates all configuration files in the home directory of the user. This may be a problem if you need to simultaneously build with @@ -227,23 +191,20 @@ root directory instead of the user home directory. Use this option also when it is desirable to run emsdk in a fully portable mode that does not touch any files outside the emsdk directory. -##### How do I track the latest Emscripten development with the SDK? +### How do I track the latest Emscripten development with the SDK? A common and supported use case of the Emscripten SDK is to enable the workflow where you directly interact with the github repositories. This allows you to obtain new features and latest fixes immediately as they are pushed to the github repository, without having to wait for release to be tagged. You do not need a github account or a fork of Emscripten to do this. To switch to using the -latest upstream git development branch `incoming`, run the following: +latest upstream git development branch `main`, run the following: emsdk install git-1.9.4 # Install git. Skip if the system already has it. - emsdk install sdk-incoming-64bit # Clone+pull the latest kripken/emscripten/incoming. - emsdk activate sdk-incoming-64bit # Set the incoming SDK as the currently active one. + emsdk install sdk-upstream-main-64bit # Clone+pull the latest emscripten-core/emscripten/main. + emsdk activate sdk-upstream-main-64bit # Set the main SDK as the currently active one. -If you want to use the upstream stable branch `master`, then replace -`-incoming-` with `-master-` above. - -##### How do I use my own Emscripten github fork with the SDK? +### How do I use my own Emscripten github fork with the SDK? It is also possible to use your own fork of the Emscripten repository via the SDK. This is achieved with standard git machinery, so there if you are already @@ -251,22 +212,22 @@ acquainted with working on multiple remotes in a git clone, these steps should be familiar to you. This is useful in the case when you want to make your own modifications to the Emscripten toolchain, but still keep using the SDK environment and tools. To set up your own fork as the currently active -Emscripten toolchain, first install the `sdk-incoming` SDK like shown in the +Emscripten toolchain, first install the `sdk-main` SDK like shown in the previous section, and then run the following commands in the emsdk directory: - cd emscripten/incoming + cd emscripten/main # Add a git remote link to your own repository. git remote add myremote https://github.com/mygituseraccount/emscripten.git # Obtain the changes in your link. git fetch myremote - # Switch the emscripten-incoming tool to use your fork. - git checkout -b myincoming --track myremote/incoming + # Switch the emscripten-main tool to use your fork. + git checkout -b mymain --track myremote/main In this way you can utilize the Emscripten SDK tools while using your own git fork. You can switch back and forth between remotes via the `git checkout` command as usual. -##### How do I use Emscripten SDK with a custom version of python, java, node.js or some other tool? +### How do I use Emscripten SDK with a custom version of python, java, node.js or some other tool? The provided Emscripten SDK targets are metapackages that refer to a specific set of tools that have been tested to work together. For example, @@ -280,98 +241,18 @@ clang-e1.35.0-64bit emscripten-1.35.0` will only install the Emscripten LLVM/Clang compiler and the Emscripten frontend without supplying python and node.js. -##### My installation fails with "fatal error: ld terminated with signal 9 [Killed]"? +### My installation fails with "fatal error: ld terminated with signal 9 [Killed]"? This may happen if the system runs out of memory. If you are attempting to build -one of the packages from source and are running in a virtual OS or have +one of the packages from source and are running in a virtual OS or may have relatively little RAM and disk space available, then the build might fail. Try feeding your computer more memory. Another thing to try is to force emsdk install to build in a singlethreaded mode, which will require less RAM simultaneously. To do this, pass the `-j1` flag to the `emsdk install` command. -## Uninstalling the Emscripten SDK - -If you installed the SDK using an NSIS installer on Windows, launch 'Control -Panel' -> 'Uninstall a program' -> 'Emscripten SDK'. - -If you want to remove a Portable SDK, just delete the directory where you put -the Portable SDK into. - -## Platform-Specific Notes - -##### Mac OS X - -* On OS X (and Linux), the git tool will not be installed automatically. Git is - not a required core component, and is only needed if you want to use one of - the development branches emscripten-incoming or emscripten-master directly, - instead of the fixed releases. To install git on OS X, you can - - 1. Install XCode, and in XCode, install XCode Command Line Tools. This will - provide git to the system PATH. For more help on this step, see - http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools - 2. Install git directly from http://git-scm.com/ - -* Also, on OS X, `java` is not bundled with the Emscripten SDK. After installing - emscripten via emsdk, typing 'emcc --help' should pop up a OS X dialog "Java - is not installed. To open java, you need a Java SE 6 runtime. Would you like - to install one now?" that will automatically download a Java runtime to the - system. - -##### Linux - -* On Linux, emsdk does not interact with Linux package managers on the behalf of - the user, nor does it install any tools to the system. All file changes are - done inside the `emsdk/` directory. - -* Emsdk does not provide `python`, `node` or `java` on Linux. The user is - expected to install these beforehand with the system package manager. - -##### Windows - -* On Windows, if you want to build any of the packages from source (instead of - using the precompiled ones), you will need git, CMake and Visual Studio 2015. - Git can be installed via emsdk by typing "emsdk install git-1.9.4", CMake can - be found from http://www.cmake.org/, and Visual Studio can be installed from - https://www.visualstudio.com. - -###### How do I run Emscripten on 32-bit Windows? +### How do I run Emscripten on 32-bit systems or non-x86-64 systems? -Emscripten SDK releases are no longer packaged or maintained for 32-bit Windows. +Emscripten SDK releases are no longer packaged or maintained for 32-bit systems. If you want to run Emscripten on a 32-bit system, you can try manually building -the compiler for 32-bit mode. Follow the steps in the above section "Building an -Emscripten tag or branch from source" to get started. - -### Archived Releases - -You can always install old SDK and compiler toolchains via the latest emsdk. If -you need to fall back to an old version, download the Portable SDK version and -use that to install a previous version of a tool. All old tool versions are -available by typing `emsdk list --old`. - -On Windows, you can install one of the **old versions** via an offline NSIS -installer: - - - [emsdk-1.5.6.1-full.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.5.6.1-full.exe) (32-bit, first emsdk release) - - [emsdk-1.5.6.2-full-32bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.5.6.2-full-32bit.exe) - - [emsdk-1.5.6.2-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.5.6.2-full-64bit.exe) - - [emsdk-1.7.8-full-32bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.7.8-full-32bit.exe) - - [emsdk-1.7.8-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.7.8-full-64bit.exe) - - [emsdk-1.8.2-full-32bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.8.2-full-32bit.exe) - - [emsdk-1.8.2-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.8.2-full-64bit.exe) - - [emsdk-1.12.0-full-32bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.12.0-full-32bit.exe) - - [emsdk-1.12.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.12.0-full-64bit.exe) (the last non-fastcomp version with Clang 3.2) - - [emsdk-1.13.0-full-32bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.13.0-full-64bit.exe) (a unstable first fastcomp release with Clang 3.3) - - [emsdk-1.16.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.16.0-full-64bit.exe) (first stable fastcomp release) - - [emsdk-1.21.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.21.0-full-64bit.exe) - - [emsdk-1.22.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.22.0-full-64bit.exe) - - [emsdk-1.25.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.25.0-full-64bit.exe) - - [emsdk-1.27.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.27.0-full-64bit.exe) (last release based on Clang 3.3) - - [emsdk-1.29.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.29.0-full-64bit.exe) (the only release based on Clang 3.4) - - [emsdk-1.30.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.30.0-full-64bit.exe) (the only release based on Clang 3.5) - - [emsdk-1.34.1-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.34.1-full-64bit.exe) (first release based on Clang 3.7) - - [emsdk-1.35.0-full-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.35.0-full-64bit.exe) - - [emsdk-1.35.0-web-64bit.exe](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.35.0-web-64bit.exe) - - [emsdk-1.35.0-portable-64bit.zip](https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-1.35.0-portable-64bit.zip) - -Snapshots of all tagged Emscripten compiler releases (not full SDKs) can be -found at [emscripten/releases](https://github.com/kripken/emscripten/releases). +the compiler. Follow the steps in the above section "Building an Emscripten tag +or branch from source" to get started. diff --git a/bazel/BUILD b/bazel/BUILD new file mode 100644 index 0000000000..9aa46d9379 --- /dev/null +++ b/bazel/BUILD @@ -0,0 +1,44 @@ +package(default_visibility = ['//visibility:public']) + +config_setting( + name = "linux", + constraint_values = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], +) + +config_setting( + name = "macos", + constraint_values = [ + "@platforms//os:macos", + "@platforms//cpu:x86_64", + ], +) + +config_setting( + name = "windows", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], +) + +alias( + name = "binaries", + actual = select({ + ":linux": "@emscripten_bin_linux//:all", + ":macos": "@emscripten_bin_mac//:all", + ":windows": "@emscripten_bin_win//:all", + }), +) + +alias( + name = "node_modules", + actual = select({ + ":linux": "@emscripten_npm_linux//:node_modules", + ":macos": "@emscripten_npm_mac//:node_modules", + ":windows": "@emscripten_npm_win//:node_modules", + }), +) + diff --git a/bazel/README.md b/bazel/README.md new file mode 100644 index 0000000000..dc849fd27a --- /dev/null +++ b/bazel/README.md @@ -0,0 +1,61 @@ +# Bazel Emscripten toolchain + +## Setup Instructions + +In `WORKSPACE` file, put: +``` +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "emsdk", + strip_prefix = "emsdk-c1589b55641787d55d53e883852035beea9aec3f/bazel", + url = "https://github.com/emscripten-core/emsdk/archive/c1589b55641787d55d53e883852035beea9aec3f.tar.gz", + sha256 = "7a58a9996b113d3e0675df30b5f17e28aa47de2e684a844f05394fe2f6f12e8e", +) + +load("@emsdk//:deps.bzl", emsdk_deps = "deps") +emsdk_deps() + +load("@emsdk//:emscripten_deps.bzl", emsdk_emscripten_deps = "emscripten_deps") +emsdk_emscripten_deps() +``` + +## Building + +### Using --config=wasm + +Put the following lines into your `.bazelrc`: +``` +build:wasm --crosstool_top=//emscripten_toolchain:everything +build:wasm --cpu=wasm +build:wasm --host_crosstool_top=@bazel_tools//tools/cpp:toolchain +``` + +Simply pass `--config=wasm` when building a normal `cc_binary`. The result of +this build will be a tar archive containing any files produced by emscripten. + +### Using wasm_cc_binary +First, write a new rule wrapping your `cc_binary`. + +``` +load("@rules_cc//cc:defs.bzl", "cc_binary") +load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary") + +cc_binary( + name = "hello-world", + srcs = ["hello-world.cc"], +) + +wasm_cc_binary( + name = "hello-world-wasm", + cc_target = ":hello-world", +) +``` + +Now you can run `bazel build :hello-world-wasm`. The result of this build will +be the individual files produced by emscripten. Note that some of these files +may be empty. This is because bazel has no concept of optional outputs for +rules. + +`wasm_cc_binary` uses transition to use emscripten toolchain on `cc_target` +and all of its dependencies, and does not require amending `.bazelrc`. This +is the preferred way, since it also unpacks the resulting tarball. diff --git a/bazel/WORKSPACE b/bazel/WORKSPACE new file mode 100644 index 0000000000..22311ff4a0 --- /dev/null +++ b/bazel/WORKSPACE @@ -0,0 +1,7 @@ +workspace(name = "emsdk") + +load(":deps.bzl", "deps") +deps() + +load(":emscripten_deps.bzl", "emscripten_deps") +emscripten_deps() diff --git a/bazel/bazelrc b/bazel/bazelrc new file mode 100644 index 0000000000..85801e8381 --- /dev/null +++ b/bazel/bazelrc @@ -0,0 +1,5 @@ +build:wasm --crosstool_top=//emscripten_toolchain:everything + +build:wasm --cpu=wasm + +build:wasm --host_crosstool_top=@bazel_tools//tools/cpp:toolchain diff --git a/bazel/deps.bzl b/bazel/deps.bzl new file mode 100644 index 0000000000..0b37e1fdb8 --- /dev/null +++ b/bazel/deps.bzl @@ -0,0 +1,11 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +def deps(): + excludes = native.existing_rules().keys() + + if "build_bazel_rules_nodejs" not in excludes: + http_archive( + name = "build_bazel_rules_nodejs", + sha256 = "0f2de53628e848c1691e5729b515022f5a77369c76a09fbe55611e12731c90e3", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.0.1/rules_nodejs-2.0.1.tar.gz"], + ) diff --git a/bazel/emscripten_deps.bzl b/bazel/emscripten_deps.bzl new file mode 100644 index 0000000000..e6a30beea0 --- /dev/null +++ b/bazel/emscripten_deps.bzl @@ -0,0 +1,76 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@build_bazel_rules_nodejs//:index.bzl", "npm_install") +load(":revisions.bzl", "EMSCRIPTEN_TAGS") + +def _parse_version(v): + return [int(u) for u in v.split(".")] + +def emscripten_deps(emscripten_version = "latest"): + version = emscripten_version + + if version == "latest": + version = reversed(sorted(EMSCRIPTEN_TAGS.keys(), key=_parse_version))[0] + + if version not in EMSCRIPTEN_TAGS.keys(): + error_msg = "Emscripten version {} not found.".format(version) + error_msg += " Look at @emsdk//:revisions.bzl for the list " + error_msg += "of currently supported versions." + fail(error_msg) + + revision = EMSCRIPTEN_TAGS[version] + + emscripten_url = "https://storage.googleapis.com/webassembly/emscripten-releases-builds/{}/{}/wasm-binaries.tbz2" + + # This could potentially backfire for projects with multiple emscripten + # dependencies that use different emscripten versions + excludes = native.existing_rules().keys() + if "emscripten_bin_linux" not in excludes: + http_archive( + name = "emscripten_bin_linux", + strip_prefix = "install", + url = emscripten_url.format("linux", revision.hash), + sha256 = revision.sha_linux, + build_file = "@emsdk//emscripten_toolchain:emscripten.BUILD", + type = "tar.bz2", + ) + + if "emscripten_bin_mac" not in excludes: + http_archive( + name = "emscripten_bin_mac", + strip_prefix = "install", + url = emscripten_url.format("mac", revision.hash), + sha256 = revision.sha_mac, + build_file = "@emsdk//emscripten_toolchain:emscripten.BUILD", + type = "tar.bz2", + ) + + if "emscripten_bin_win" not in excludes: + http_archive( + name = "emscripten_bin_win", + strip_prefix = "install", + url = emscripten_url.format("win", revision.hash), + sha256 = revision.sha_win, + build_file = "@emsdk//emscripten_toolchain:emscripten.BUILD", + type = "tar.bz2", + ) + + if "emscripten_npm_linux" not in excludes: + npm_install( + name = "emscripten_npm_linux", + package_json = "@emscripten_bin_linux//:emscripten/package.json", + package_lock_json = "@emscripten_bin_linux//:emscripten/package-lock.json", + ) + + if "emscripten_npm_mac" not in excludes: + npm_install( + name = "emscripten_npm_mac", + package_json = "@emscripten_bin_mac//:emscripten/package.json", + package_lock_json = "@emscripten_bin_mac//:emscripten/package-lock.json", + ) + + if "emscripten_npm_win" not in excludes: + npm_install( + name = "emscripten_npm_win", + package_json = "@emscripten_bin_win//:emscripten/package.json", + package_lock_json = "@emscripten_bin_win//:emscripten/package-lock.json", + ) diff --git a/bazel/emscripten_toolchain/BUILD.bazel b/bazel/emscripten_toolchain/BUILD.bazel new file mode 100644 index 0000000000..488da5a027 --- /dev/null +++ b/bazel/emscripten_toolchain/BUILD.bazel @@ -0,0 +1,81 @@ +load(":crosstool.bzl", "emscripten_cc_toolchain_config_rule") + +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "common-script-includes", + srcs = [ + "emar.sh", + "emcc.sh", + "emscripten_config", + "env.sh", + "@nodejs//:node_files", + "@emsdk//:binaries", + "@emsdk//:node_modules", + ], +) + +filegroup( + name = "compile-emscripten", + srcs = [":common-script-includes"], +) + +filegroup( + name = "link-emscripten", + srcs = [ + "emcc_link.sh", + "link_wrapper.py", + ":common-script-includes", + "@emsdk//:binaries", + "@nodejs//:node_files", + ], +) + +filegroup( + name = "every-file", + srcs = [ + ":compile-emscripten", + ":link-emscripten", + "@emsdk//:binaries", + "@nodejs//:node_files", + ], +) + +filegroup(name = "empty") + +# dlmalloc.bc is implictly added by the emscripten toolchain +cc_library(name = "malloc") + +emscripten_cc_toolchain_config_rule( + name = "wasm", + cpu = "wasm", + em_config = "emscripten_config", + emscripten_binaries = "@emsdk//:binaries", +) + +cc_toolchain( + name = "cc-compiler-wasm", + all_files = ":every-file", + ar_files = ":common-script-includes", + as_files = ":empty", + compiler_files = ":compile-emscripten", + dwp_files = ":empty", + linker_files = ":link-emscripten", + objcopy_files = ":empty", + strip_files = ":empty", + toolchain_config = "wasm", + toolchain_identifier = "emscripten-wasm", +) + +cc_toolchain_suite( + name = "everything", + toolchains = { + "wasm": ":cc-compiler-wasm", + "wasm|emscripten": ":cc-compiler-wasm", + }, +) + +py_binary( + name = "wasm_binary", + srcs = ["wasm_binary.py"], +) diff --git a/bazel/emscripten_toolchain/crosstool.bzl b/bazel/emscripten_toolchain/crosstool.bzl new file mode 100644 index 0000000000..0672ef7597 --- /dev/null +++ b/bazel/emscripten_toolchain/crosstool.bzl @@ -0,0 +1,1110 @@ +"""This module encapsulates logic to create emscripten_cc_toolchain_config rule.""" + +load( + "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", + "action_config", + "env_entry", + "env_set", + "feature", + "feature_set", + "flag_group", + "tool", + "tool_path", + "variable_with_value", + "with_feature_set", + _flag_set = "flag_set", +) +load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") + +def flag_set(flags = None, features = None, not_features = None, **kwargs): + """Extension to flag_set which allows for a "simple" form. + + The simple form allows specifying flags as a simple list instead of a flag_group + if enable_if or expand_if semantics are not required. + + Similarly, the simple form allows passing features/not_features if they are a simple + list of semantically "and" features. + (i.e. "asan" and "dbg", rather than "asan" or "dbg") + + Args: + flags: list, set of flags + features: list, set of features required to be enabled. + not_features: list, set of features required to not be enabled. + **kwargs: The rest of the args for flag_set. + + Returns: + flag_set + """ + if flags: + if kwargs.get("flag_groups"): + fail("Cannot set flags and flag_groups") + else: + kwargs["flag_groups"] = [flag_group(flags = flags)] + + if features or not_features: + if kwargs.get("with_features"): + fail("Cannot set features/not_feature and with_features") + kwargs["with_features"] = [with_feature_set( + features = features or [], + not_features = not_features or [], + )] + return _flag_set(**kwargs) + +CROSSTOOL_DEFAULT_WARNINGS = [ + "-Wall", +] + +def _impl(ctx): + target_cpu = ctx.attr.cpu + toolchain_identifier = "emscripten-" + target_cpu + target_system_name = target_cpu + "-unknown-emscripten" + + host_system_name = "i686-unknown-linux-gnu" + + target_libc = "musl/js" + + abi_version = "emscripten_syscalls" + + compiler = "emscripten" + abi_libc_version = "default" + + cc_target_os = "emscripten" + + emscripten_dir = ctx.attr.emscripten_binaries.label.workspace_root + + builtin_sysroot = emscripten_dir + "/emscripten/cache/sysroot" + + ################################################################ + # Tools + ################################################################ + clang_tool = tool(path = "emcc.sh") + clif_match_tool = tool(path = "dummy_clif_matcher") + link_tool = tool(path = "emcc_link.sh") + archive_tool = tool(path = "emar.sh") + strip_tool = tool(path = "NOT_USED_STRIP_TOOL") + + #### Legacy tool paths (much of this is redundant with action_configs, but + #### these are still used for some things) + tool_paths = [ + tool_path(name = "ar", path = "emar.sh"), + tool_path(name = "cpp", path = "/bin/false"), + tool_path(name = "gcc", path = "emcc.sh"), + tool_path(name = "gcov", path = "/bin/false"), + tool_path(name = "ld", path = "emcc_link.sh"), + tool_path(name = "nm", path = "NOT_USED"), + tool_path(name = "objdump", path = "/bin/false"), + tool_path(name = "strip", path = "NOT_USED"), + ] + + ################################################################ + # Action Configs + ################################################################ + + cpp_compile_action = action_config( + action_name = ACTION_NAMES.cpp_compile, + tools = [clang_tool], + ) + + cpp_module_compile_action = action_config( + action_name = ACTION_NAMES.cpp_module_compile, + tools = [clang_tool], + ) + + cpp_module_codegen_action = action_config( + action_name = ACTION_NAMES.cpp_module_codegen, + tools = [clang_tool], + ) + + clif_match_action = action_config( + action_name = ACTION_NAMES.clif_match, + tools = [clif_match_tool], + ) + + cpp_link_dynamic_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_dynamic_library, + tools = [link_tool], + ) + + strip_action = action_config( + action_name = ACTION_NAMES.strip, + tools = [strip_tool], + ) + + preprocess_assemble_action = action_config( + action_name = ACTION_NAMES.preprocess_assemble, + tools = [clang_tool], + ) + + cpp_header_parsing_action = action_config( + action_name = ACTION_NAMES.cpp_header_parsing, + tools = [clang_tool], + ) + + cpp_link_static_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_static_library, + enabled = True, + flag_sets = [ + flag_set( + flag_groups = [ + flag_group( + flags = ["rcsD", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + flag_set( + flag_groups = [ + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + flag_set( + flag_groups = [ + flag_group( + flags = ["@%{linker_param_file}"], + expand_if_available = "linker_param_file", + ), + ], + ), + ], + tools = [archive_tool], + ) + + c_compile_action = action_config( + action_name = ACTION_NAMES.c_compile, + tools = [clang_tool], + ) + + linkstamp_compile_action = action_config( + action_name = ACTION_NAMES.linkstamp_compile, + tools = [clang_tool], + ) + + assemble_action = action_config( + action_name = ACTION_NAMES.assemble, + tools = [clang_tool], + ) + + cpp_link_executable_action = action_config( + action_name = ACTION_NAMES.cpp_link_executable, + tools = [link_tool], + ) + + cpp_link_nodeps_dynamic_library_action = action_config( + action_name = ACTION_NAMES.cpp_link_nodeps_dynamic_library, + tools = [link_tool], + ) + + action_configs = [ + strip_action, + c_compile_action, + cpp_compile_action, + linkstamp_compile_action, + assemble_action, + preprocess_assemble_action, + cpp_header_parsing_action, + cpp_module_compile_action, + cpp_module_codegen_action, + cpp_link_executable_action, + cpp_link_dynamic_library_action, + cpp_link_nodeps_dynamic_library_action, + cpp_link_static_library_action, + clif_match_action, + ] + + all_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.lto_backend, + ] + + all_cpp_compile_actions = [ + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ] + + preprocessor_compile_actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.clif_match, + ] + + all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ] + + ################################################################ + # Features + ################################################################ + + features = [ + # This set of magic "feature"s are important configuration information for blaze. + feature(name = "no_legacy_features", enabled = True), + feature( + name = "has_configured_linker_path", + enabled = True, + ), + + # Blaze requests this feature by default, but we don't care. + feature(name = "dependency_file"), + + # Blaze requests this feature by default, but we don't care. + feature(name = "random_seed"), + + # Formerly "needsPic" attribute + feature(name = "supports_pic", enabled = False), + + # Blaze requests this feature by default. + # Blaze also tests if this feature is supported, before setting the "pic" build-variable. + feature(name = "pic"), + + # Blaze requests this feature if fission is requested + # Blaze also tests if it's supported to see if we support fission. + feature(name = "per_object_debug_info"), + + # Blaze requests this feature by default. + # Blaze also tests if this feature is supported before setting preprocessor_defines + # (...but why?) + feature(name = "preprocessor_defines"), + + # Blaze requests this feature by default. + # Blaze also tests if this feature is supported before setting includes. (...but why?) + feature(name = "include_paths"), + + # Blaze tests if this feature is enabled in order to create implicit + # "nodeps" .so outputs from cc_library rules. + feature(name = "supports_dynamic_linker", enabled = False), + + # Blaze requests this feature when linking a cc_binary which is + # "dynamic" aka linked against nodeps-dynamic-library cc_library + # outputs. + feature(name = "dynamic_linking_mode"), + + #### Configuration features + feature( + name = "crosstool_cpu", + enabled = True, + implies = ["crosstool_cpu_" + target_cpu], + ), + feature( + name = "crosstool_cpu_asmjs", + provides = ["variant:crosstool_cpu"], + ), + feature( + name = "crosstool_cpu_wasm", + provides = ["variant:crosstool_cpu"], + ), + + # These 3 features will be automatically enabled by blaze in the + # corresponding build mode. + feature( + name = "opt", + provides = ["variant:crosstool_build_mode"], + ), + feature( + name = "dbg", + provides = ["variant:crosstool_build_mode"], + ), + feature( + name = "fastbuild", + provides = ["variant:crosstool_build_mode"], + ), + + #### User-settable features + + # Set if enabling exceptions. + feature(name = "exceptions"), + + # This feature overrides the default optimization to prefer execution speed + # over binary size (like clang -O3). + feature( + name = "optimized_for_speed", + provides = ["variant:crosstool_optimization_mode"], + ), + + # This feature overrides the default optimization to prefer binary size over + # execution speed (like clang -Oz). + feature( + name = "optimized_for_size", + provides = ["variant:crosstool_optimization_mode"], + ), + + # Convenience aliases / alt-spellings. + feature( + name = "optimize_for_speed", + implies = ["optimized_for_speed"], + ), + feature( + name = "optimize_for_size", + implies = ["optimized_for_size"], + ), + + # This feature allows easier use of profiling tools by preserving mangled + # C++ names. This does everything profiling_funcs does and more. + feature(name = "profiling"), + + # This feature emits only enough debug info for function names to appear + # in profiles. + feature(name = "profiling_funcs"), + + # This feature allows source maps to be generated. + feature( + name = "source_maps", + implies = ["full_debug_info"], + ), + feature( + name = "dwarf_debug_info", + implies = ["profiling"], + ), + + # Turns on full debug info (-g4). + feature(name = "full_debug_info"), + + # Enables the use of "Emscripten" Pthread implementation. + # https://kripken.github.io/emscripten-site/docs/porting/pthreads.html + # https://github.com/kripken/emscripten/wiki/Pthreads-with-WebAssembly + feature(name = "use_pthreads"), + + # If enabled, the runtime will exit when main() completes. + feature(name = "exit_runtime"), + + # Primarily for toolchain maintainers: + feature(name = "emcc_debug"), + feature(name = "emcc_debug_link"), + feature( + name = "llvm_backend", + requires = [feature_set(features = ["crosstool_cpu_wasm"])], + enabled = True, + ), + + # Remove once flag is flipped. + # See https://github.com/bazelbuild/bazel/issues/7687 + feature( + name = "do_not_split_linking_cmdline", + ), + + # Adds simd support, only available with the llvm backend. + feature( + name = "wasm_simd", + requires = [feature_set(features = ["llvm_backend"])], + ), + feature( + name = "precise_long_double_printf", + enabled = True, + ), + feature( + name = "wasm_warnings_as_errors", + enabled = True, + ), + + # ASan and UBSan. See also: + # https://emscripten.org/docs/debugging/Sanitizers.html + feature(name = "wasm_asan"), + feature(name = "wasm_ubsan"), + + feature( + name = "output_format_js", + enabled = True, + ), + ] + + crosstool_default_flag_sets = [ + # Compile, Link, and CC_FLAGS make variable + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flag_groups = [ + flag_group( + flags = ["--sysroot=%{sysroot}"], + expand_if_available = "sysroot", + ), + ], + ), + # Compile + Link + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + # This forces color diagnostics even on Forge (where we don't have an + # attached terminal). + flags = [ + "-fdiagnostics-color", + ], + ), + # C++ compiles (and implicitly link) + flag_set( + actions = all_cpp_compile_actions, + flags = [ + "-fno-exceptions", + ], + not_features = ["exceptions"], + ), + flag_set( + actions = all_cpp_compile_actions, + flags = [ + "-fexceptions", + ], + features = ["exceptions"], + ), + # All compiles (and implicitly link) + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = [ + "-fno-strict-aliasing", + "-funsigned-char", + "-no-canonical-prefixes", + ], + ), + # Language Features + flag_set( + actions = all_cpp_compile_actions, + flags = ["-std=gnu++17", "-nostdinc", "-nostdinc++",], + ), + + # Emscripten-specific settings: + flag_set( + actions = all_compile_actions + all_link_actions, + flags = ["-s", "WASM=0"], + features = ["crosstool_cpu_asmjs"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-s", "USE_PTHREADS=1"], + features = ["use_pthreads"], + ), + flag_set( + actions = all_link_actions, + flags = ["-s", "EXIT_RUNTIME=1"], + features = ["exit_runtime"], + ), + flag_set( + actions = all_compile_actions + all_link_actions, + flags = ["-pthread"], + features = ["llvm_backend", "use_pthreads"], + ), + flag_set( + actions = all_compile_actions + all_link_actions, + flags = ["-msimd128"], + features = ["wasm_simd"], + ), + flag_set( + actions = all_link_actions, + flags = ["-s", "PRINTF_LONG_DOUBLE=1"], + features = ["precise_long_double_printf"], + ), + flag_set( + actions = all_link_actions, + flags = ["--oformat=js"], + features = ["output_format_js"], + ), + + # Opt + flag_set( + actions = preprocessor_compile_actions, + flags = ["-DNDEBUG"], + features = ["opt"], + ), + flag_set( + actions = all_compile_actions, + flags = ["-fomit-frame-pointer"], + features = ["opt"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-O3"], + features = ["opt"], + ), + # Users can override opt-level with semantic names... + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-Oz"], + features = ["optimized_for_size", "opt"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-O3"], + features = ["optimized_for_speed", "opt"], + ), + + # Fastbuild + flag_set( + actions = all_compile_actions, + flags = ["-fomit-frame-pointer"], + features = ["fastbuild"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-O2"], + features = ["fastbuild"], + ), + + # Dbg + flag_set( + actions = all_compile_actions, + flags = ["-fno-omit-frame-pointer"], + features = ["dbg"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-g", "-O0"], + features = ["dbg"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = [ + "-g4", + "-fsanitize=address", + "-O1", + "-DADDRESS_SANITIZER=1", + "-fno-omit-frame-pointer", + ], + features = ["wasm_asan"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = [ + "-g4", + "-fsanitize=undefined", + "-O1", + "-DUNDEFINED_BEHAVIOR_SANITIZER=1", + "-fno-omit-frame-pointer", + "-fno-sanitize=vptr", + ], + features = ["wasm_ubsan"], + ), + + # Profiling provides full debug info and a special --profiling flag + # to control name mangling + flag_set( + actions = all_link_actions, + flags = ["--profiling"], + features = ["profiling"], + ), + flag_set( + actions = all_link_actions, + flags = ["--profiling_funcs"], + features = ["profiling_funcs"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-g4"], + features = ["full_debug_info"], + ), + flag_set( + actions = all_link_actions, + flags = ["-gseparate-dwarf"], + features = ["dwarf_debug_info"], + ), + flag_set( + actions = all_compile_actions + + all_link_actions, + flags = ["-fdebug-compilation-dir=."], + features = ["dwarf_debug_info"], + ), + # Generic warning flag list + flag_set( + actions = all_compile_actions, + flags = CROSSTOOL_DEFAULT_WARNINGS, + ), + + # Defines and Includes and Paths and such + flag_set( + actions = all_compile_actions, + flag_groups = [ + flag_group(flags = ["-fPIC"], expand_if_available = "pic"), + ], + ), + flag_set( + actions = preprocessor_compile_actions, + flag_groups = [ + flag_group( + flags = ["-D%{preprocessor_defines}"], + iterate_over = "preprocessor_defines", + ), + ], + ), + flag_set( + actions = preprocessor_compile_actions, + flag_groups = [ + flag_group( + flags = ["-include", "%{includes}"], + iterate_over = "includes", + expand_if_available = "includes", + ), + ], + ), + flag_set( + actions = preprocessor_compile_actions, + flag_groups = [ + flag_group( + flags = ["-iquote", "%{quote_include_paths}"], + iterate_over = "quote_include_paths", + ), + flag_group( + flags = ["-I%{include_paths}"], + iterate_over = "include_paths", + ), + flag_group( + flags = ["-isystem", "%{system_include_paths}"], + iterate_over = "system_include_paths", + ), + ], + ), + + ## Linking options (not libs -- those go last) + + # Generic link options + flag_set( + actions = [ + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, + ], + flags = ["-shared"], + ), + + # Linker search paths and objects: + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + iterate_over = "runtime_library_search_directories", + flag_groups = [ + flag_group( + flags = [ + "-Wl,-rpath,$EXEC_ORIGIN/%{runtime_library_search_directories}", + ], + expand_if_true = "is_cc_test", + ), + flag_group( + flags = [ + "-Wl,-rpath,$ORIGIN/%{runtime_library_search_directories}", + ], + expand_if_false = "is_cc_test", + ), + ], + expand_if_available = "runtime_library_search_directories", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-L%{library_search_directories}"], + iterate_over = "library_search_directories", + expand_if_available = "library_search_directories", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + # This is actually a list of object files from the linkstamp steps + flags = ["%{linkstamp_paths}"], + iterate_over = "linkstamp_paths", + expand_if_available = "linkstamp_paths", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["@%{thinlto_param_file}"], + expand_if_available = "libraries_to_link", + expand_if_true = "thinlto_param_file", + ), + flag_group( + iterate_over = "libraries_to_link", + flag_groups = [ + flag_group( + flags = ["-Wl,--start-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["-Wl,-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["%{libraries_to_link.object_files}"], + iterate_over = "libraries_to_link.object_files", + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "interface_library", + ), + ), + flag_group( + flags = ["%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "static_library", + ), + ), + flag_group( + flags = ["-l%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "dynamic_library", + ), + ), + flag_group( + flags = ["-l:%{libraries_to_link.name}"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "versioned_dynamic_library", + ), + ), + flag_group( + flags = ["-Wl,-no-whole-archive"], + expand_if_true = "libraries_to_link.is_whole_archive", + ), + flag_group( + flags = ["-Wl,--end-lib"], + expand_if_equal = variable_with_value( + name = "libraries_to_link.type", + value = "object_file_group", + ), + ), + ], + expand_if_available = "libraries_to_link", + ), + ], + ), + + # Configure the header parsing and preprocessing. + flag_set( + actions = [ACTION_NAMES.cpp_header_parsing], + flags = ["-xc++-header", "-fsyntax-only"], + features = ["parse_headers"], + ), + + # Note: user compile flags should be nearly last -- you probably + # don't want to put any more features after this! + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["%{user_compile_flags}"], + iterate_over = "user_compile_flags", + expand_if_available = "user_compile_flags", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["%{user_link_flags}"], + iterate_over = "user_link_flags", + expand_if_available = "user_link_flags", + ), + ], + ), + ## Options which need to go late -- after all the user options -- go here. + flag_set( + # One might hope that these options would only be needed for C++ + # compiles. But, sadly, users compile ".c" files with custom + # copts=["-x", "c++"], and expect that to be able to find C++ stdlib + # headers. It might be worth pondering how blaze could support this sort + # of use-case better. + actions = preprocessor_compile_actions + + [ACTION_NAMES.cc_flags_make_variable], + flags = [ + "-iwithsysroot" + "/include/c++/v1", + "-iwithsysroot" + "/include/compat", + "-iwithsysroot" + "/include", + "-isystem", emscripten_dir + "/lib/clang/13.0.0/include", + ], + ), + # Inputs and outputs + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-MD", "-MF", "%{dependency_file}"], + expand_if_available = "dependency_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-c", "%{source_file}"], + expand_if_available = "source_file", + ), + ], + ), + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ACTION_NAMES.linkstamp_compile, + ACTION_NAMES.assemble, + ACTION_NAMES.preprocess_assemble, + ACTION_NAMES.cpp_header_parsing, + ACTION_NAMES.cpp_module_compile, + ACTION_NAMES.cpp_module_codegen, + ACTION_NAMES.clif_match, + ], + flag_groups = [ + flag_group( + flags = ["-S"], + expand_if_available = "output_assembly_file", + ), + flag_group( + flags = ["-E"], + expand_if_available = "output_preprocess_file", + ), + flag_group( + flags = ["-o", "%{output_file}"], + expand_if_available = "output_file", + ), + ], + ), + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["-o", "%{output_execpath}"], + expand_if_available = "output_execpath", + ), + ], + ), + # And finally, the params file! + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = ["@%{linker_param_file}"], + expand_if_available = "linker_param_file", + ), + ], + ), + flag_set( + actions = all_compile_actions, + flags = [ + "-Wno-builtin-macro-redefined", + # Genrules may not escape quotes enough for these, so + # don't put them into $(CC_FLAGS): + '-D__DATE__="redacted"', + '-D__TIMESTAMP__="redacted"', + '-D__TIME__="redacted"', + ], + ), + flag_set( + actions = all_compile_actions, + flags = ["-Werror"], + features = ["wasm_warnings_as_errors"], + ), + ] + + crosstool_default_env_sets = [ + # Globals + env_set( + actions = all_compile_actions + + all_link_actions + + [ACTION_NAMES.cpp_link_static_library], + env_entries = [ + env_entry( + key = "EM_BIN_PATH", + value = emscripten_dir, + ), + env_entry( + key = "EM_CONFIG_PATH", + value = ctx.file.em_config.path, + ), + ], + ), + # Use llvm backend. Off by default, enabled via --features=llvm_backend + env_set( + actions = all_compile_actions + + all_link_actions + + [ACTION_NAMES.cpp_link_static_library], + env_entries = [env_entry(key = "EMCC_WASM_BACKEND", value = "1")], + with_features = [with_feature_set(features = ["llvm_backend"])], + ), + # Debug compile and link. Off by default, enabled via --features=emcc_debug + env_set( + actions = all_compile_actions, + env_entries = [env_entry(key = "EMCC_DEBUG", value = "1")], + with_features = [with_feature_set(features = ["emcc_debug"])], + ), + + # Debug only link step. Off by default, enabled via --features=emcc_debug_link + env_set( + actions = all_link_actions, + env_entries = [env_entry(key = "EMCC_DEBUG", value = "1")], + with_features = [ + with_feature_set(features = ["emcc_debug"]), + with_feature_set(features = ["emcc_debug_link"]), + ], + ), + ] + + crosstool_default_flags_feature = feature( + name = "crosstool_default_flags", + enabled = True, + flag_sets = crosstool_default_flag_sets, + env_sets = crosstool_default_env_sets, + ) + + features.append(crosstool_default_flags_feature) + + cxx_builtin_include_directories = [ + emscripten_dir + "/emscripten/cache/sysroot/include/c++/v1", + emscripten_dir + "/emscripten/cache/sysroot/include/compat", + emscripten_dir + "/emscripten/cache/sysroot/include", + emscripten_dir + "/lib/clang/13.0.0/include", + ] + + artifact_name_patterns = [] + + make_variables = [] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + action_configs = action_configs, + artifact_name_patterns = artifact_name_patterns, + cxx_builtin_include_directories = cxx_builtin_include_directories, + toolchain_identifier = toolchain_identifier, + host_system_name = host_system_name, + target_system_name = target_system_name, + target_cpu = target_cpu, + target_libc = target_libc, + compiler = compiler, + abi_version = abi_version, + abi_libc_version = abi_libc_version, + tool_paths = tool_paths, + make_variables = make_variables, + builtin_sysroot = builtin_sysroot, + cc_target_os = cc_target_os, + ) + +emscripten_cc_toolchain_config_rule = rule( + implementation = _impl, + attrs = { + "cpu": attr.string(mandatory = True, values = ["asmjs", "wasm"]), + "em_config": attr.label(mandatory = True, allow_single_file=True), + "emscripten_binaries": attr.label(mandatory = True), + }, + provides = [CcToolchainConfigInfo], +) diff --git a/bazel/emscripten_toolchain/emar.sh b/bazel/emscripten_toolchain/emar.sh new file mode 100755 index 0000000000..e4279f16a1 --- /dev/null +++ b/bazel/emscripten_toolchain/emar.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source external/emsdk/emscripten_toolchain/env.sh + +exec python3 $EMSCRIPTEN/emar.py "$@" diff --git a/bazel/emscripten_toolchain/emcc.sh b/bazel/emscripten_toolchain/emcc.sh new file mode 100755 index 0000000000..7f3699be1d --- /dev/null +++ b/bazel/emscripten_toolchain/emcc.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source external/emsdk/emscripten_toolchain/env.sh + +exec python3 $EMSCRIPTEN/emcc.py "$@" diff --git a/bazel/emscripten_toolchain/emcc_link.sh b/bazel/emscripten_toolchain/emcc_link.sh new file mode 100755 index 0000000000..24d806d029 --- /dev/null +++ b/bazel/emscripten_toolchain/emcc_link.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source external/emsdk/emscripten_toolchain/env.sh + +exec python3 external/emsdk/emscripten_toolchain/link_wrapper.py "$@" diff --git a/bazel/emscripten_toolchain/emscripten.BUILD b/bazel/emscripten_toolchain/emscripten.BUILD new file mode 100644 index 0000000000..6f11852874 --- /dev/null +++ b/bazel/emscripten_toolchain/emscripten.BUILD @@ -0,0 +1,6 @@ +package(default_visibility = ['//visibility:public']) + +filegroup( + name = "all", + srcs = glob(["**"]), +) diff --git a/bazel/emscripten_toolchain/emscripten_config b/bazel/emscripten_toolchain/emscripten_config new file mode 100644 index 0000000000..d1275ff978 --- /dev/null +++ b/bazel/emscripten_toolchain/emscripten_config @@ -0,0 +1,12 @@ +import os +import platform + +ROOT_DIR = os.environ["ROOT_DIR"] +EMSCRIPTEN_ROOT = os.environ["EMSCRIPTEN"] +BINARYEN_ROOT = ROOT_DIR + "/" + os.environ["EM_BIN_PATH"] +LLVM_ROOT = BINARYEN_ROOT + "/bin" +FROZEN_CACHE = True + +system = platform.system() +nodejs_binary = "node.exe" if(system =="Windows") else "bin/node" +NODE_JS = ROOT_DIR + "/external/nodejs_{}_amd64/{}".format(system.lower(), nodejs_binary) diff --git a/bazel/emscripten_toolchain/env.sh b/bazel/emscripten_toolchain/env.sh new file mode 100755 index 0000000000..d66a3a1fde --- /dev/null +++ b/bazel/emscripten_toolchain/env.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +export ROOT_DIR=`(pwd -P)` +export EMSCRIPTEN=$ROOT_DIR/$EM_BIN_PATH/emscripten +export EM_CONFIG=$ROOT_DIR/$EM_CONFIG_PATH diff --git a/bazel/emscripten_toolchain/link_wrapper.py b/bazel/emscripten_toolchain/link_wrapper.py new file mode 100644 index 0000000000..1e26bde7f5 --- /dev/null +++ b/bazel/emscripten_toolchain/link_wrapper.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +"""wrapper around emcc link step. + +This wrapper currently serves the following purposes. + +1. When building with --config=wasm the final output is multiple files, usually + at least one .js and one .wasm file. Since the cc_binary link step only + allows a single output, we must tar up the outputs into a single file. + +2. Add quotes around arguments that need them in the response file to work + around a bazel quirk. + +3. Ensure the external_debug_info section of the wasm points at the correct + bazel path. +""" + +from __future__ import print_function + +import argparse +import os +import subprocess +import sys + +# Only argument should be @path/to/parameter/file +assert sys.argv[1][0] == '@' +param_filename = sys.argv[1][1:] +param_file_args = [l.strip() for l in open(param_filename, 'r').readlines()] + +# Re-write response file if needed. +if any(' ' in a for a in param_file_args): + new_param_filename = param_filename + '.modified' + with open(new_param_filename, 'w') as f: + for param in param_file_args: + if ' ' in param: + f.write('"%s"' % param) + else: + f.write(param) + f.write('\n') + sys.argv[1] = '@' + new_param_filename + +emcc_py = os.path.join(os.environ['EMSCRIPTEN'], 'emcc.py') +rtn = subprocess.call(['python3', emcc_py] + sys.argv[1:]) +if rtn != 0: + sys.exit(1) + +# Parse the arguments that we gave to the linker to determine what the output +# file is named and what the output format is. +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument('-o') +parser.add_argument('--oformat') +options = parser.parse_known_args(param_file_args)[0] +output_file = options.o +oformat = options.oformat +outdir = os.path.dirname(output_file) +base_name = os.path.basename(output_file) + +# The output file name is the name of the build rule that was built. +# Add an appropriate file extension based on --oformat. +if oformat is not None: + base_name_split = os.path.splitext(base_name) + + # If the output name has no extension, give it the appropriate extension. + if not base_name_split[1]: + os.rename(output_file, output_file + '.' + oformat) + + # If the output name does have an extension and it matches the output format, + # change the base_name so it doesn't have an extension. + elif base_name_split[1] == '.' + oformat: + base_name = base_name_split[0] + + # If the output name does have an extension and it does not match the output + # format, change the base_name so it doesn't have an extension and rename + # the output_file so it has the proper extension. + # Note that if you do something like name your build rule "foo.js" and pass + # "--oformat=html", emscripten will write to the same file for both the js and + # html output, overwriting the js output entirely with the html. + # Please don't do that. + else: + base_name = base_name_split[0] + os.rename(output_file, os.path.join(outdir, base_name + '.' + oformat)) + +files = [] +extensions = [ + '.js', + '.wasm', + '.wasm.map', + '.js.mem', + '.fetch.js', + '.worker.js', + '.data', + '.js.symbols', + '.wasm.debug.wasm', + '.html' +] + +for ext in extensions: + filename = base_name + ext + if os.path.exists(os.path.join(outdir, filename)): + files.append(filename) + +wasm_base = os.path.join(outdir, base_name + '.wasm') +if os.path.exists(wasm_base + '.debug.wasm') and os.path.exists(wasm_base): + # If we have a .wasm.debug.wasm file and a .wasm file, we need to rewrite the + # section in the .wasm file that refers to it. The path that's in there + # is the blaze output path; we want it to be just the filename. + + llvm_objcopy = os.path.join( + os.environ['EMSCRIPTEN'], 'llvm-bin/llvm-objcopy') + # First, check to make sure the .wasm file has the header that needs to be + # rewritten. + rtn = subprocess.call([ + llvm_objcopy, + '--dump-section=external_debug_info=/dev/null', + wasm_base], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if rtn == 0: + # If llvm-objcopy did not return an error, the external_debug_info section + # must exist, so we're good to continue. + + # Next we need to convert length of the filename to LEB128. + # Start by converting the length of the filename to a bit string. + bit_string = '{0:b}'.format(len(base_name + '.wasm.debug.wasm')) + + # Pad the bit string with 0s so that its length is a multiple of 7. + while len(bit_string) % 7 != 0: + bit_string = '0' + bit_string + + # Break up our bit string into chunks of 7. + # We do this backwards because the final format is little-endian. + final_bytes = bytearray() + for i in reversed(range(0, len(bit_string), 7)): + binary_part = bit_string[i:i + 7] + if i != 0: + # Every chunk except the last one needs to be prepended with '1'. + # The length of each chunk is 7, so that one has an implicit '0'. + binary_part = '1' + binary_part + final_bytes.append(int(binary_part, 2)) + # Finally, add the actual filename. + final_bytes.extend((base_name + '.wasm.debug.wasm').encode()) + + # Write our length + filename bytes to a temp file. + with open('debugsection.tmp', 'wb+') as f: + f.write(final_bytes) + f.close() + + # First delete the old section. + subprocess.check_call([ + llvm_objcopy, + wasm_base, + '--remove-section=external_debug_info']) + # Rewrite section with the new size and filename from the temp file. + subprocess.check_call([ + llvm_objcopy, + wasm_base, + '--add-section=external_debug_info=debugsection.tmp']) + +# If we have more than one output file then create tarball +if len(files) > 1: + cmd = ['tar', 'cf', 'tmp.tar'] + files + subprocess.check_call(cmd, cwd=outdir) + os.rename(os.path.join(outdir, 'tmp.tar'), output_file) +elif len(files) == 1: + # Otherwise, if only have a single output than move it to the expected name + if files[0] != os.path.basename(output_file): + os.rename(os.path.join(outdir, files[0]), output_file) +else: + print('emcc.py did not appear to output any known files!') + sys.exit(1) + +sys.exit(0) diff --git a/bazel/emscripten_toolchain/wasm_binary.py b/bazel/emscripten_toolchain/wasm_binary.py new file mode 100644 index 0000000000..641c0d6f28 --- /dev/null +++ b/bazel/emscripten_toolchain/wasm_binary.py @@ -0,0 +1,84 @@ +"""Unpackages a bazel emscripten archive for use in a bazel BUILD rule. + +This script will take a tar archive containing the output of the emscripten +toolchain. This file contains any output files produced by a wasm_cc_binary or a +cc_binary built with --config=wasm. The files are extracted into the given +output path. + +The name of archive is expected to be of the format `foo` or `foo.XXX` and +the contents are expected to be foo.js and foo.wasm. + +Several optional files may also be in the archive, including but not limited to +foo.js.mem, pthread-main.js, and foo.wasm.map. + +If the file is not a tar archive, the passed file will simply be copied to its +destination. + +This script and its accompanying Bazel rule should allow you to extract a +WebAssembly binary into a larger web application. +""" + +import argparse +import os +import subprocess +import sys + + +def ensure(f): + if not os.path.exists(f): + with open(f, 'w'): + pass + + +def check(f): + if not os.path.exists(f): + raise Exception('Expected file in archive: %s' % f) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--archive', help='The archive to extract from.') + parser.add_argument('--output_path', help='The path to extract into.') + args = parser.parse_args() + + basename = os.path.basename(args.archive) + stem = basename.split('.')[0] + + # Check the type of the input file + mimetype_bytes = subprocess.check_output(['file', '-Lb', '--mime-type', '--mime-encoding', args.archive]) + mimetype = mimetype_bytes.decode(sys.stdout.encoding) + + # If we have a tar, extract all files. If we have just a single file, copy it. + if 'tar' in mimetype: + subprocess.check_call( + ['tar', 'xf', args.archive, '-C', args.output_path]) + elif 'binary' in mimetype: + subprocess.check_call([ + 'cp', + args.archive, + os.path.join(args.output_path, stem + '.wasm')]) + elif 'text' in mimetype: + subprocess.check_call([ + 'cp', + args.archive, + os.path.join(args.output_path, stem + '.js')]) + else: + subprocess.check_call(['cp', args.archive, args.output_path]) + + # At least one of these two files should exist at this point. + ensure(os.path.join(args.output_path, stem + '.js')) + ensure(os.path.join(args.output_path, stem + '.wasm')) + + # And can optionally contain these extra files. + ensure(os.path.join(args.output_path, stem + '.wasm.map')) + ensure(os.path.join(args.output_path, stem + '.worker.js')) + ensure(os.path.join(args.output_path, stem + '.js.mem')) + ensure(os.path.join(args.output_path, stem + '.data')) + ensure(os.path.join(args.output_path, stem + '.fetch.js')) + ensure(os.path.join(args.output_path, stem + '.js.symbols')) + ensure(os.path.join(args.output_path, stem + '.wasm.debug.wasm')) + ensure(os.path.join(args.output_path, stem + '.html')) + + +if __name__ == '__main__': + main() diff --git a/bazel/emscripten_toolchain/wasm_cc_binary.bzl b/bazel/emscripten_toolchain/wasm_cc_binary.bzl new file mode 100644 index 0000000000..9d6804117b --- /dev/null +++ b/bazel/emscripten_toolchain/wasm_cc_binary.bzl @@ -0,0 +1,152 @@ +"""wasm_cc_binary rule for compiling C++ targets to WebAssembly. +""" + +def _wasm_transition_impl(settings, attr): + _ignore = (settings, attr) + + features = list(settings["//command_line_option:features"]) + linkopts = list(settings["//command_line_option:linkopt"]) + + if attr.threads == "emscripten": + # threads enabled + features.append("use_pthreads") + elif attr.threads == "off": + # threads disabled + features.append("-use_pthreads") + + if attr.exit_runtime == True: + features.append("exit_runtime") + + if attr.backend == "llvm": + features.append("llvm_backend") + elif attr.backend == "emscripten": + features.append("-llvm_backend") + + if attr.simd: + features.append("wasm_simd") + + return { + "//command_line_option:compiler": "emscripten", + "//command_line_option:crosstool_top": "@emsdk//emscripten_toolchain:everything", + "//command_line_option:cpu": "wasm", + "//command_line_option:features": features, + "//command_line_option:dynamic_mode": "off", + "//command_line_option:linkopt": linkopts, + "//command_line_option:platforms": [], + "//command_line_option:custom_malloc": "@emsdk//emscripten_toolchain:malloc", + } + +_wasm_transition = transition( + implementation = _wasm_transition_impl, + inputs = [ + "//command_line_option:features", + "//command_line_option:linkopt", + ], + outputs = [ + "//command_line_option:compiler", + "//command_line_option:cpu", + "//command_line_option:crosstool_top", + "//command_line_option:features", + "//command_line_option:dynamic_mode", + "//command_line_option:linkopt", + "//command_line_option:platforms", + "//command_line_option:custom_malloc", + ], +) + +def _wasm_binary_impl(ctx): + cc_target = ctx.attr.cc_target[0] + + args = [ + "--output_path={}".format(ctx.outputs.loader.dirname), + ] + [ + ctx.expand_location("--archive=$(location {})".format( + cc_target.label, + ), [cc_target]), + ] + outputs = [ + ctx.outputs.loader, + ctx.outputs.wasm, + ctx.outputs.map, + ctx.outputs.mem, + ctx.outputs.fetch, + ctx.outputs.worker, + ctx.outputs.data, + ctx.outputs.symbols, + ctx.outputs.dwarf, + ctx.outputs.html, + ] + + ctx.actions.run( + inputs = ctx.files.cc_target, + outputs = outputs, + arguments = args, + executable = ctx.executable._wasm_binary_extractor, + ) + + return DefaultInfo( + files = depset(outputs), + # This is needed since rules like web_test usually have a data + # dependency on this target. + data_runfiles = ctx.runfiles(transitive_files = depset(outputs)), + ) + +def _wasm_binary_outputs(name, cc_target): + basename = cc_target.name + basename = basename.split(".")[0] + outputs = { + "loader": "{}/{}.js".format(name, basename), + "wasm": "{}/{}.wasm".format(name, basename), + "map": "{}/{}.wasm.map".format(name, basename), + "mem": "{}/{}.js.mem".format(name, basename), + "fetch": "{}/{}.fetch.js".format(name, basename), + "worker": "{}/{}.worker.js".format(name, basename), + "data": "{}/{}.data".format(name, basename), + "symbols": "{}/{}.js.symbols".format(name, basename), + "dwarf": "{}/{}.wasm.debug.wasm".format(name, basename), + "html": "{}/{}.html".format(name, basename), + } + + return outputs + +# Wraps a C++ Blaze target, extracting the appropriate files. +# +# This rule will transition to the emscripten toolchain in order +# to build the the cc_target as a WebAssembly binary. +# +# Args: +# name: The name of the rule. +# cc_target: The cc_binary or cc_library to extract files from. +wasm_cc_binary = rule( + implementation = _wasm_binary_impl, + attrs = { + "backend": attr.string( + default = "_default", + values = ["_default", "emscripten", "llvm"], + ), + "cc_target": attr.label( + cfg = _wasm_transition, + mandatory = True, + ), + "exit_runtime": attr.bool( + default = False, + ), + "threads": attr.string( + default = "_default", + values = ["_default", "emscripten", "off"], + ), + "simd": attr.bool( + default = False, + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + "_wasm_binary_extractor": attr.label( + executable = True, + allow_files = True, + cfg = "exec", + default = Label("@emsdk//emscripten_toolchain:wasm_binary"), + ), + }, + outputs = _wasm_binary_outputs, +) diff --git a/bazel/emscripten_toolchain/wasm_rules.bzl b/bazel/emscripten_toolchain/wasm_rules.bzl new file mode 100644 index 0000000000..f8dce22eec --- /dev/null +++ b/bazel/emscripten_toolchain/wasm_rules.bzl @@ -0,0 +1,6 @@ +"""Rules related to C++ and WebAssembly. +""" + +load(":wasm_cc_binary.bzl", _wasm_cc_binary = "wasm_cc_binary") + +wasm_cc_binary = _wasm_cc_binary diff --git a/bazel/hello-world/BUILD b/bazel/hello-world/BUILD new file mode 100644 index 0000000000..07fbed5510 --- /dev/null +++ b/bazel/hello-world/BUILD @@ -0,0 +1,23 @@ +load("@rules_cc//cc:defs.bzl", "cc_binary") +load("//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary") + +cc_binary( + name = "hello-world", + srcs = ["hello-world.cc"], +) + +cc_binary( + name = "hello-world-simd", + srcs = ["hello-world-simd.cc"], +) + +wasm_cc_binary( + name = "hello-world-wasm", + cc_target = ":hello-world", +) + +wasm_cc_binary( + name = "hello-world-wasm-simd", + cc_target = ":hello-world-simd", + simd = True, +) diff --git a/bazel/hello-world/hello-world-simd.cc b/bazel/hello-world/hello-world-simd.cc new file mode 100644 index 0000000000..649adab20a --- /dev/null +++ b/bazel/hello-world/hello-world-simd.cc @@ -0,0 +1,10 @@ +#include + +void multiply_arrays(int* out, int* in_a, int* in_b, int size) { + for (int i = 0; i < size; i += 4) { + v128_t a = wasm_v128_load(&in_a[i]); + v128_t b = wasm_v128_load(&in_b[i]); + v128_t prod = wasm_i32x4_mul(a, b); + wasm_v128_store(&out[i], prod); + } +} diff --git a/bazel/hello-world/hello-world.cc b/bazel/hello-world/hello-world.cc new file mode 100644 index 0000000000..ee72c53171 --- /dev/null +++ b/bazel/hello-world/hello-world.cc @@ -0,0 +1,6 @@ +#include + +int main(int argc, char** argv) { + std::cout << "hello world!" << std::endl; + return 0; +} diff --git a/bazel/revisions.bzl b/bazel/revisions.bzl new file mode 100644 index 0000000000..eae75d13d3 --- /dev/null +++ b/bazel/revisions.bzl @@ -0,0 +1,41 @@ +# This file is automatically updated by emsdk/scripts/update_bazel_workspace.sh +# DO NOT MODIFY + +EMSCRIPTEN_TAGS = { + "2.0.18": struct( + hash = "c2ac7520fad29a7937ed60ab6a95b08eb374c7ba", + sha_linux = "e9f777de592f606b10104b2efe5179a7a8f44e3a9dffa1e3aaf73e05eb8893d7", + sha_mac = "86b1dd62e424e3788bf132292a694a25ca9b0875d06f50d0f5d424593697452c", + sha_win = "49ce07bda6be070251db44a08fcc05cae21ffdbd7522423a0c79bde635e87e28", + ), + "2.0.17": struct( + hash = "f5c45e60392b82f603e3a8039c62db294fab02d2", + sha_linux = "b40a4874057e4cace600f8ee9787dcbe236e3dc5b2fff5c2ecb0e867e426f99c", + sha_mac = "081f61abf7d5ac0ec31aaffc5550013d4093ea4ea39520b7a32b7448d2a6ee70", + sha_win = "45d06e597e6a1185a76200bd0481495e7298800a4805045d9cdbcce6311c91b2", + ), + "2.0.16": struct( + hash = "80d9674f2fafa6b9346d735c42d5c52b8cc8aa8e", + sha_linux = "e527638b224d9a30dc7e5fa4b9bd2eb2ab76ad306739ba8cacf5a5e333933a2a", + sha_mac = "061020eb0e3ee0611dc5a0008ccc7778168a4f838d49ca41c0aad8c52c1a01c9", + sha_win = "99364ed0388f928e0594f790662bf3a30c2894b0eff81797e1b64f62128561cb", + ), + "2.0.15": struct( + hash = "89202930a98fe7f9ed59b574469a9471b0bda7dd", + sha_linux = "7ff49fc63adf29970f6e7af1df445d7f554bdbbb2606db1cb5d3567ce69df1db", + sha_mac = "e35cced1514ad0da40584f8dd6f76aabf847ce0fa82c6dc8dd9442fb74ed6d0d", + sha_win = "31d5f8107c87833cea57edc57613bba4b36b16152772f744c5ad204594b4e666", + ), + "2.0.14": struct( + hash = "fc5562126762ab26c4757147a3b4c24e85a7289e", + sha_linux = "e466cd47ddd4bf0acd645412fdf08eda6d232484e48e5a2643e08062a7a4cf56", + sha_mac = "1c554c08459b7025638ca4eddba0d35babe8c26b202a70a74e9442d577896211", + sha_win = "428bc6094671937af96f26d803871fc5cd83d4d2b1c1df45fa6873a9bc5cac51", + ), + "2.0.13": struct( + hash = "ce0e4a4d1cab395ee5082a60ebb4f3891a94b256", + sha_linux = "8986ed886e111c661099c5147126b8a379a4040aab6a1f572fe01f0f9b99a343", + sha_mac = "88c91332c8c76fed14ebf0edc9a08f586012f54f04ad61e5b1b6d02bf96bdeab", + sha_win = "9fb3b945b7bd56e34d17ec04de4cce475f26c49d161aee9d9c0b8b1434591f88", + ), +} diff --git a/bazel/test_external/BUILD b/bazel/test_external/BUILD new file mode 100644 index 0000000000..73568cf388 --- /dev/null +++ b/bazel/test_external/BUILD @@ -0,0 +1,12 @@ +load("@emsdk//emscripten_toolchain:wasm_rules.bzl", "wasm_cc_binary") + +cc_binary( + name = "hello-world", + srcs = ["hello-world.cc"], +) + +wasm_cc_binary( + name = "hello-world-wasm", + cc_target = ":hello-world", +) + diff --git a/bazel/test_external/WORKSPACE b/bazel/test_external/WORKSPACE new file mode 100644 index 0000000000..f0a446c24e --- /dev/null +++ b/bazel/test_external/WORKSPACE @@ -0,0 +1,10 @@ +local_repository( + name = "emsdk", + path = "..", +) + +load("@emsdk//:deps.bzl", "deps") +deps() + +load("@emsdk//:emscripten_deps.bzl", "emscripten_deps") +emscripten_deps() diff --git a/bazel/test_external/hello-world.cc b/bazel/test_external/hello-world.cc new file mode 100644 index 0000000000..ee72c53171 --- /dev/null +++ b/bazel/test_external/hello-world.cc @@ -0,0 +1,6 @@ +#include + +int main(int argc, char** argv) { + std::cout << "hello world!" << std::endl; + return 0; +} diff --git a/bin/elevate.exe b/bin/elevate.exe deleted file mode 100644 index 06d3cfbfe1..0000000000 Binary files a/bin/elevate.exe and /dev/null differ diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..c80c23c3e3 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,134 @@ +FROM ubuntu:focal AS stage_build + +ARG EMSCRIPTEN_VERSION=tot +ENV EMSDK /emsdk + +# ------------------------------------------------------------------------------ + +RUN echo "## Start building" \ + && echo "## Update and install packages" \ + && apt-get -qq -y update \ + && apt-get -qq install -y --no-install-recommends \ + binutils \ + build-essential \ + ca-certificates \ + file \ + git \ + python3 \ + python3-pip \ + && echo "## Done" + +# Copy the contents of this repository to the container +COPY . ${EMSDK} + +RUN echo "## Install Emscripten" \ + && cd ${EMSDK} \ + && ./emsdk install ${EMSCRIPTEN_VERSION} \ + && echo "## Done" + +# This generates configuration that contains all valid paths according to installed SDK +# TODO(sbc): We should be able to use just emcc -v here but it doesn't +# currently create the sanity file. +RUN cd ${EMSDK} \ + && echo "## Generate standard configuration" \ + && ./emsdk activate ${EMSCRIPTEN_VERSION} \ + && chmod 777 ${EMSDK}/upstream/emscripten \ + && chmod -R 777 ${EMSDK}/upstream/emscripten/cache \ + && echo "int main() { return 0; }" > hello.c \ + && ${EMSDK}/upstream/emscripten/emcc -c hello.c \ + && cat ${EMSDK}/upstream/emscripten/cache/sanity.txt \ + && echo "## Done" + +# Cleanup Emscripten installation and strip some symbols +RUN echo "## Aggressive optimization: Remove debug symbols" \ + && cd ${EMSDK} && . ./emsdk_env.sh \ + # Remove debugging symbols from embedded node (extra 7MB) + && strip -s `which node` \ + # Tests consume ~80MB disc space + && rm -fr ${EMSDK}/upstream/emscripten/tests \ + # Fastcomp is not supported + && rm -fr ${EMSDK}/upstream/fastcomp \ + # strip out symbols from clang (~extra 50MB disc space) + && find ${EMSDK}/upstream/bin -type f -exec strip -s {} + || true \ + && echo "## Done" + +# ------------------------------------------------------------------------------ +# -------------------------------- STAGE DEPLOY -------------------------------- +# ------------------------------------------------------------------------------ + +FROM ubuntu:focal AS stage_deploy + +COPY --from=stage_build /emsdk /emsdk + +# Fallback in case Emscripten isn't activated. +# This will let use tools offered by this image inside other Docker images +# (sub-stages) or with custom / no entrypoint +ENV EMSDK=/emsdk \ + EM_CONFIG=/emsdk/.emscripten \ + EMSDK_NODE=/emsdk/node/14.15.5_64bit/bin/node \ + PATH="/emsdk:/emsdk/upstream/emscripten:/emsdk/upstream/bin:/emsdk/node/14.15.5_64bit/bin:${PATH}" + +# ------------------------------------------------------------------------------ +# Create a 'standard` 1000:1000 user +# Thanks to that this image can be executed as non-root user and created files +# will not require root access level on host machine Please note that this +# solution even if widely spread (i.e. Node.js uses it) is far from perfect as +# user 1000:1000 might not exist on host machine, and in this case running any +# docker image will cause other random problems (mostly due `$HOME` pointing to +# `/`) +RUN echo "## Create emscripten user (1000:1000)" \ + && groupadd --gid 1000 emscripten \ + && useradd --uid 1000 --gid emscripten --shell /bin/bash --create-home emscripten \ + && echo "umask 0000" >> /etc/bash.bashrc \ + && echo ". /emsdk/emsdk_env.sh" >> /etc/bash.bashrc \ + && echo "## Done" + +# ------------------------------------------------------------------------------ + +RUN echo "## Update and install packages" \ + && apt-get -qq -y update \ + # Somewhere in here apt sets up tzdata which asks for your time zone and blocks + # waiting for the answer which you can't give as docker build doesn't read from + # the terninal. The env vars set here avoid the interactive prompt and set the TZ. + && DEBIAN_FRONTEND="noninteractive" TZ="America/San_Francisco" apt-get -qq install -y --no-install-recommends \ + sudo \ + libxml2 \ + ca-certificates \ + python3 \ + python3-pip \ + wget \ + curl \ + zip \ + unzip \ + git \ + git-lfs \ + ssh-client \ + build-essential \ + make \ + ant \ + libidn11 \ + cmake \ + openjdk-11-jre-headless \ + # Standard Cleanup on Debian images + && apt-get -y clean \ + && apt-get -y autoclean \ + && apt-get -y autoremove \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /var/cache/debconf/*-old \ + && rm -rf /usr/share/doc/* \ + && rm -rf /usr/share/man/?? \ + && rm -rf /usr/share/man/??_* \ + && echo "## Done" + +# ------------------------------------------------------------------------------ +# Use commonly used /src as working directory +WORKDIR /src + +LABEL maintainer="kontakt@trzeci.eu" \ + org.label-schema.name="emscripten" \ + org.label-schema.description="The official container with Emscripten SDK" \ + org.label-schema.url="https://emscripten.org" \ + org.label-schema.vcs-url="https://github.com/emscripten-core/emsdk" \ + org.label-schema.docker.dockerfile="/docker/Dockerfile" + +# ------------------------------------------------------------------------------ diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 0000000000..0c961ca56b --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,28 @@ +# A Makefile to build, test, tag and publish the Emscripten SDK Docker container. + +# Emscripten version to build: Should match the version that has been already released. +# i.e.: 1.39.18 +version = +alias = + +image_name ?= emscripten/emsdk + +.TEST: +ifndef version + $(error argument 'version' is not set. Please call `make version=SOME_VERSION ...`) +endif + +build: Dockerfile .TEST + cd .. && docker build --network host --build-arg=EMSCRIPTEN_VERSION=${version} -t ${image_name}:${version} -f docker/$< . + +test: test_dockerimage.sh .TEST + # test as non-root + docker run --rm -u `id -u`:`id -g` -w /emsdk/docker --net=host ${image_name}:${version} \ + bash $< + +push: .TEST + docker push ${image_name}:${version} +ifdef alias + docker tag ${image_name}:${version} ${image_name}:${alias} + docker push ${image_name}:${alias} +endif diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..7b9d301483 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,125 @@ +# Dockerfile for EMSDK + +This Dockerfile builds a self-contained version of Emscripten SDK that enables Emscripten to be used without any +other installation on the host system. + +It is published at https://hub.docker.com/u/emscripten/emsdk + +### Usage + +Simple usage of this container to compile a hello-world +```bash +# create helloworld.cpp +cat << EOF > helloworld.cpp +#include +int main() { + std::cout << "Hello World!" << std::endl; + return 0; +} +EOF +``` + +```bash +# compile with docker image +docker run \ + --rm \ + -v $(pwd):/src \ + -u $(id -u):$(id -g) \ + emscripten/emsdk \ + emcc helloworld.cpp -o helloworld.js + +# execute on host machine +node helloworld.js +``` + +Teardown of compilation command: + +|part|description| +|---|---| +|`docker run`| A standard command to run a command in a container| +|`--rm`|remove a container after execution (optimization)| +|`-v $(pwd):$(pwd)`|Mounting current folder from the host system into mirrored path on the container
TIP: This helps to investigate possible problem as we preserve exactly the same paths like in host. In such case modern editors (like Sublime, Atom, VS Code) let us to CTRL+Click on a problematic file | +|`-u $(id -u):$(id -g)`| Run the container as a non-root user with the same UID and GID as local user. Hence all files produced by this are accessible to non-root users| +|`emscripten/emsdk`|Get the latest tag of this container| +|`emcc helloworld.cpp -o helloworld.js`|Execute `emcc` command with following arguments inside container, effectively compile our source code| + + + +### Building Dockerfile + +This image has following optional arguments + +| arg | default value | description | +| --- | --- | --- | +| `EMSCRIPTEN_VERSION` | `tot`
(special case, tip-of-tree) | One of released version of Emscripten. For example `2.0.0`
Minimal supported version is **1.39.0** | + +**Building** + +This step will build Dockerfile as given tag on local machine +```bash +# using docker +docker build \ + --network host \ + --build-arg=EMSCRIPTEN_VERSION=1.39.17 \ + -t emscripten/emsdk:1.39.17 \ + -f docker/Dockerfile \ + . +``` +```bash +# using predefined make target +make version=1.39.17 build test +``` + +**Tagging** + +In case of using `docker build` command directly, given `--tag` should match version of released Emscripten (you can see list of non-legacy versions by executing `emsdk list`). + +**Pushing** + +This step will take local image and push to default docker registry. You need to make sure that you logged in docker cli (`docker login`) and you have rights to push to that registry. + +```bash +# using docker +docker push emscripten/emsdk:1.39.17 +``` +```bash +# using predefined make target +make version=1.39.17 push +``` + +In case of pushing the most recent version, this version should be also tagged as `latest` and pushed. +```bash +# using docker cli +docker tag emscripten/emsdk:1.39.17 emscripten/emsdk:latest +docker push emscripten/emsdk:latest + +```bash +# using make +make version=1.39.17 alias=latest push +``` + +### Extending + +If your project uses packages that this image doesn't provide you might want to: +* Contribute to this repo: Maybe your dependency is either non-intrusive or could be useful for other people +* Create custom image that bases on this image + +1. create own Dockerfile that holds: + ```dockerfile + # Point at any base image that you find suitable to extend. + FROM emscripten/emsdk:1.39.17 + + # Install required tools that are useful for your project i.e. ninja-build + RUN apt update && apt install -y ninja-build + ``` + +2. build it + ```bash + docker build -t extended_emscripten . + ``` + +3. test + ```bash + docker run --rm extended_emscripten ninja --version + # 1.10.0 + ``` diff --git a/docker/test_dockerimage.sh b/docker/test_dockerimage.sh new file mode 100755 index 0000000000..4f44806c5f --- /dev/null +++ b/docker/test_dockerimage.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -ex + +if [ $EUID -eq 0 ]; then + sudo -u nobody `which emcc` --version +fi + +which llvm-ar +which emsdk +node --version +npm --version +python3 --version +pip3 --version +em++ --version +emcc --version +java -version +cmake --version + +exit_code=0 + +# test emcc compilation +echo 'int main() { return 0; }' | emcc -o /tmp/main.js -xc - +node /tmp/main.js || exit_code=$? +if [ $exit_code -ne 0 ]; then + echo "Node exited with non-zero exit code: $exit_code" + exit $exit_code +fi + +# test embuilder +embuilder build zlib --force diff --git a/emscripten-releases-tags.txt b/emscripten-releases-tags.txt index 8a8d672d5f..7865a835b4 100644 --- a/emscripten-releases-tags.txt +++ b/emscripten-releases-tags.txt @@ -1,7 +1,63 @@ { - "latest": "1.38.33", + "latest": "2.0.18", "releases": { + "2.0.18": "c2ac7520fad29a7937ed60ab6a95b08eb374c7ba", + "2.0.17": "f5c45e60392b82f603e3a8039c62db294fab02d2", + "2.0.16": "80d9674f2fafa6b9346d735c42d5c52b8cc8aa8e", + "2.0.15": "89202930a98fe7f9ed59b574469a9471b0bda7dd", + "2.0.14": "fc5562126762ab26c4757147a3b4c24e85a7289e", + "2.0.13": "ce0e4a4d1cab395ee5082a60ebb4f3891a94b256", + "2.0.12": "dcf819a7821f8db0c8f15ac336fea8960ec204f5", + "2.0.11": "4764c5c323a474f7ba28ae991b0c9024fccca43c", + "2.0.10": "37fc7647c754ac9a28ad588c143b82286de0ef71", + "2.0.9": "d8e430f9a9b6e87502f826c39e7684852f59624f", + "2.0.8": "e4ed6c79f4db8b175d9bbe55869b697aba9bcf2a", + "2.0.7": "d7a29d82b320e471203b69d43aaf03b560eedc54", + "2.0.6": "4ba921c8c8fe2e8cae071ca9889d5c27f5debd87", + "2.0.5": "461f0f118d8d8e6cfd84e077f3eb010c17a39032", + "2.0.4": "eefeb3e623af023844ac477d70d1fd8a668f5110", + "2.0.3": "7a7f38ca19da152d4cd6da4776921a0f1e3f3e3f", + "2.0.2": "ede25d889a0abe63360d4c5d420087c8753b8bbe", + "2.0.1": "13e29bd55185e3c12802bc090b4507901856b2ba", + "2.0.0": "5974288502aab433d45f53511e961aaca4079d86", + "1.40.1": "536568644fd67d53778f6111fdd5f64ad3f4c539", + "1.40.0": "edf24e7233e0def312a08cc8dcec63a461155da1", + "1.39.20": "e7e39da9c81faecd9ecf44065cee864d76e4e34d", + "1.39.19": "665121d026cafc46c29b30d6d4c45ed73eedbb7e", + "1.39.18": "1914a1543f08cd8e41f44c2bb05f7a90d1920275", + "1.39.17": "9bc968c0e49b1c795ecddb87391b265911e2adcb", + "1.39.16": "ae5001fac3849895a873e422a2a80afc90f3b798", + "1.39.15": "3880c744c068986d4ee781a61f7b2e820043e11f", + "1.39.14": "574ad04affb82cc36a32dd89b2a87bea4fb30eba", + "1.39.13": "7b3cd38017f7c582cfa3ac24a9f12aa6a8dca51f", + "1.39.12": "e13b86d4dbd9a986525ef27d4ad8157949b9bc3a", + "1.39.11": "6584e2d88570ee55914db92a3bad84f99e5bdd82", + "1.39.10": "65d33d604d3fa0ebe03548378b898fc6608e9cb8", + "1.39.9": "122396dfad60e1b2a83ccefa74a1425a2e05b5cb", + "1.39.8": "9e60f34accb4627d7358223862a7e74291886ab6", + "1.39.7": "9a89fff28cc6f75e17976fce1904b280e4beb25d", + "1.39.6": "967836071d96d9b7894e492382f5fcb96423fc07", + "1.39.5": "b3ddcab6efd749d3ed937fb452ace4e39a825842", + "1.39.4": "8bb7b0bbbca74cc58741416cc955011f22ff5ccb", + "1.39.3": "b024b71038d1291ed7ec23ecd553bf2c0c8d6da6", + "1.39.2": "c630da9163a64e08de3dd948be0a0f7a175d285b", + "1.39.1": "40f3caabcef7b52bdde63d3883462414d7a25bec", + "1.39.0": "d57bfdd6d43181501bbd3fab502d57c9073ceb49", + "1.38.48": "1290d9deb93d67c4649999a8f2c8d9167d38dc04", + "1.38.47": "bc367c257409d676e71c5511383228b7aabf1689", + "1.38.46": "c89919d252f7cea00d944bdf3bd630cd3c7e7388", + "1.38.45": "f3030d9fffcc9e1287cb6b8e72982e94ece31d71", + "1.38.44": "e5fd171371c3dcb8806a337f2dfa9b70f598f456", + "1.38.43": "737d4a07be76c15124adf3c6ef2c218123f7a67f", + "1.38.42": "05f8c01394ddd0838d3cb484ba8ca97a3efc1ac9", + "1.38.41": "5c6785a63993ae7a4d5362b32b0be9c85138fb96", + "1.38.40": "7b4b328af02eafbc857b8ca1e3d9b12dddc56ef7", + "1.38.39": "f42b12c45fd3f4c20de1321402fbc28f8fd21df1", + "1.38.38": "80bff2784f8500c1305ca69ba1d9fc84df0e401c", + "1.38.37": "0ca6c49e30bc09c3ccefc867df4196a4b4183441", + "1.38.36": "d46be49c2aab975333423122239892bd46f52bba", + "1.38.35": "98f49919f25e06fa557cbcb1321d4c10e60c87ca", + "1.38.34": "048cf9424790cc525a7ea6da340820aae226f3b9", "1.38.33": "3b8cff670e9233a6623563add831647e8689a86b" } } - diff --git a/emsdk b/emsdk index 8a69afc14a..98b8d84ac2 100755 --- a/emsdk +++ b/emsdk @@ -1,2627 +1,47 @@ -#!/usr/bin/env python - -from __future__ import print_function - -import copy -import errno -import json -import multiprocessing -import os -import os.path -import platform -import re -import shutil -import stat -import subprocess -import sys -import tempfile -import zipfile - -if sys.version_info >= (3,): - from urllib.parse import urljoin - from urllib.request import urlopen - import functools -else: - from urlparse import urljoin - from urllib2 import urlopen - -# EMSDK_DEV is a developer mode flag, which, if true, the SDK is downloaded from a 'staging' online source, -# instead of the public source. New releases are first deployed to the staging source for testing, before -# being published to the public. Don't enable this unless you develop EMSDK itself and need to access the -# staging source repository instead. -EMSDK_DEV = bool(os.getenv('EMSDK_DEV')) if os.getenv('EMSDK_DEV') is not None else False - -if EMSDK_DEV: - print('EMSDK_DEV active.') - emsdk_master_server = 'http://clb.demon.fi/emscripten_dev/' -else: - emsdk_master_server = 'https://s3.amazonaws.com/mozilla-games/emscripten/' - -emsdk_packages_url = urljoin(emsdk_master_server, 'packages/') - -emscripten_git_repo = 'https://github.com/kripken/emscripten.git' -binaryen_git_repo = 'https://github.com/WebAssembly/binaryen.git' - -# Enable this to do very verbose printing about the different steps that are being run. Useful for debugging. -VERBOSE = bool(os.getenv('EMSDK_VERBOSE')) if os.getenv('EMSDK_VERBOSE') is not None else False -TTY_OUTPUT = sys.stdout.isatty() - -POWERSHELL = bool(os.getenv('EMSDK_POWERSHELL')) - -WINDOWS = False -if os.name == 'nt' or (os.getenv('SYSTEMROOT') is not None and 'windows' in os.getenv('SYSTEMROOT').lower()) or (os.getenv('COMSPEC') is not None and 'windows' in os.getenv('COMSPEC').lower()): - WINDOWS = True - ENVPATH_SEPARATOR = ';' - -MSYS = False -if os.getenv('MSYSTEM'): - MSYS = True - if os.getenv('MSYSTEM') != 'MSYS' and os.getenv('MSYSTEM') != 'MINGW64': - print('Warning: MSYSTEM environment variable is present, and is set to "' + os.getenv('MSYSTEM') + '". This shell has not been tested with emsdk and may not work.') # https://stackoverflow.com/questions/37460073/msys-vs-mingw-internal-environment-variables - -OSX = False -if platform.mac_ver()[0] != '': - OSX = True - ENVPATH_SEPARATOR = ':' - -LINUX = False -if not OSX and (platform.system() == 'Linux' or os.name == 'posix'): - LINUX = True - ENVPATH_SEPARATOR = ':' - -# Don't saturate all cores to not steal the whole system, but be aggressive. -CPU_CORES = max(multiprocessing.cpu_count() - 1, 1) - -CMAKE_BUILD_TYPE_OVERRIDE = None - -# If true, perform a --shallow clone of git. -GIT_CLONE_SHALLOW = False - -# If true, LLVM backend is built with tests enabled, and Binaryen is built with Visual Studio static analyzer enabled. -BUILD_FOR_TESTING = False - -# If true, we will try to build WebAssembly support -ENABLE_WASM = False - -# If 'auto', assertions are decided by the build type (Release&MinSizeRel=disabled, Debug&RelWithDebInfo=enabled) -# Other valid values are 'ON' and 'OFF' -ENABLE_LLVM_ASSERTIONS = 'auto' - - -def os_name(): - if WINDOWS: return 'win' - elif LINUX: return 'linux' - elif OSX: return 'osx' - else: - raise Exception('unknown OS') - - -def to_unix_path(p): - return p.replace('\\', '/') - - -def emsdk_path(): - return to_unix_path(os.path.dirname(os.path.realpath(__file__))) - - -emscripten_config_directory = os.path.expanduser("~/") -# If .emscripten exists, we are configuring as embedded inside the emsdk directory. -if os.path.exists(os.path.join(emsdk_path(), '.emscripten')): - emscripten_config_directory = emsdk_path() - -EMSDK_SET_ENV = 'emsdk_set_env.ps1' if POWERSHELL else 'emsdk_set_env.bat' if (WINDOWS and not MSYS) else 'emsdk_set_env.sh' - - -# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'. -def which(program): - def is_exe(fpath): - return os.path.isfile(fpath) and (WINDOWS or os.access(fpath, os.X_OK)) - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - - if WINDOWS and '.' not in fname: - if is_exe(exe_file + '.exe'): - return exe_file + '.exe' - if is_exe(exe_file + '.cmd'): - return exe_file + '.cmd' - if is_exe(exe_file + '.bat'): - return exe_file + '.bat' - - return None - - -def vswhere(version): - try: - program_files = os.environ['ProgramFiles(x86)'] if 'ProgramFiles(x86)' in os.environ else os.environ['ProgramFiles'] - vswhere_path = os.path.join(program_files, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe') - output = json.loads(subprocess.check_output([vswhere_path, '-latest', '-version', '[%s.0,%s.0)' % (version, version + 1), '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', '-property', 'installationPath', '-format', 'json'])) - # Visual Studio 2017 Express is not included in the above search, and it does not have the VC.Tools.x86.x64 tool, so do a catch-all attempt as a fallback, to detect Express version. - if len(output) == 0: - output = json.loads(subprocess.check_output([vswhere_path, '-latest', '-version', '[%s.0,%s.0)' % (version, version + 1), '-products', '*', '-property', 'installationPath', '-format', 'json'])) - return str(output[0]['installationPath']) if len(output) > 0 else '' - except Exception: - return '' - - -def vs_filewhere(installation_path, platform, file): - try: - vcvarsall = os.path.join(installation_path, 'VC\\Auxiliary\\Build\\vcvarsall.bat') - env = subprocess.check_output('cmd /c "%s" %s & where %s' % (vcvarsall, platform, file)) - paths = [path[:-len(file)] for path in env.split('\r\n') if path.endswith(file)] - return paths[0] - except Exception: - return '' - - -CMAKE_GENERATOR = 'Unix Makefiles' -if WINDOWS: - # Detect which CMake generator to use when building on Windows - if '--mingw' in sys.argv: CMAKE_GENERATOR = 'MinGW Makefiles' - elif '--vs2013' in sys.argv: CMAKE_GENERATOR = 'Visual Studio 12' - elif '--vs2015' in sys.argv: CMAKE_GENERATOR = 'Visual Studio 14' - elif '--vs2017' in sys.argv: CMAKE_GENERATOR = 'Visual Studio 15' - else: - program_files = os.environ['ProgramFiles(x86)'] if 'ProgramFiles(x86)' in os.environ else os.environ['ProgramFiles'] - vs2017_exists = len(vswhere(15)) > 0 - vs2015_exists = 'VS140COMNTOOLS' in os.environ or 'VSSDK140Install' in os.environ or os.path.isdir(os.path.join(program_files, 'Microsoft Visual Studio 14.0')) - vs2013_exists = 'VS120COMNTOOLS' in os.environ or os.path.isdir(os.path.join(program_files, 'Microsoft Visual Studio 12.0')) - mingw_exists = which('mingw32-make') is not None and which('g++') is not None - if vs2015_exists: CMAKE_GENERATOR = 'Visual Studio 14' - elif vs2017_exists: CMAKE_GENERATOR = 'Visual Studio 15' # VS2017 has an LLVM build issue, see https://github.com/kripken/emscripten-fastcomp/issues/185 - elif mingw_exists: CMAKE_GENERATOR = 'MinGW Makefiles' - elif vs2013_exists: CMAKE_GENERATOR = 'Visual Studio 12' # VS2013 is no longer supported, so attempt it as a last resort if someone might want to insist using it. - else: CMAKE_GENERATOR = '' # No detected generator - - -if VERBOSE: - print('CMAKE_GENERATOR: ' + CMAKE_GENERATOR) -sys.argv = list(filter(lambda x: x not in ['--mingw', '--vs2013', '--vs2015', '--vs2017'], sys.argv)) - - -# Computes a suitable path prefix to use when building with a given generator. -def cmake_generator_prefix(): - if CMAKE_GENERATOR == 'Visual Studio 15': return '_vs2017' - elif CMAKE_GENERATOR == 'Visual Studio 14': return '_vs2015' - elif CMAKE_GENERATOR == 'MinGW Makefiles': return '_mingw' - return '' # Unix Makefiles and Visual Studio 2013 do not specify a path prefix for backwards path compatibility - - -# Removes a directory tree even if it was readonly, and doesn't throw exception on failure. -def remove_tree(d): - if VERBOSE: print('remove_tree(' + str(d) + ')') - try: - def remove_readonly_and_try_again(func, path, exc_info): - if not (os.stat(path).st_mode & stat.S_IWRITE): - os.chmod(path, stat.S_IWRITE) - func(path) - else: - raise - shutil.rmtree(d, onerror=remove_readonly_and_try_again) - except Exception as e: - if VERBOSE: print('remove_tree threw an exception, ignoring: ' + str(e)) - pass - - -def import_pywin32(): - if WINDOWS: - try: - import win32api - import win32con - return win32api, win32con - except Exception: - print('Failed to import Python Windows extensions win32api and win32con. Make sure you are using the version of python available in emsdk, or install PyWin extensions to the distribution of Python you are attempting to use. (This script was launched in python instance from "' + sys.executable + '")') - sys.exit(1) - - -def win_set_environment_variable_direct(key, value, system=True): - prev_path = os.environ['PATH'] - try: - py = find_used_python() - if py: - py_path = to_native_path(py.expand_vars(py.activated_path)) - os.environ['PATH'] = os.environ['PATH'] + ';' + py_path - win32api, win32con = import_pywin32() - if system: # Read globally from ALL USERS section. - folder = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 0, win32con.KEY_ALL_ACCESS) - else: # Register locally from CURRENT USER section. - folder = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, 'Environment', 0, win32con.KEY_ALL_ACCESS) - win32api.RegSetValueEx(folder, key, 0, win32con.REG_EXPAND_SZ, value) - if VERBOSE: print('Set key=' + key + ' with value ' + value + ' in registry.') - except Exception as e: - if e[0] == 5 or e[2] == 'Access is denied.': - print('Error! Failed to set the environment variable \'' + key + '\'! Setting environment variables permanently requires administrator access. Please rerun this command with administrative privileges. This can be done for example by holding down the Ctrl and Shift keys while opening a command prompt in start menu.') - sys.exit(1) - print('Failed to write environment variable ' + key + ':', file=sys.stderr) - print(str(e), file=sys.stderr) - win32api.RegCloseKey(folder) - os.environ['PATH'] = prev_path - return None - - win32api.RegCloseKey(folder) - os.environ['PATH'] = prev_path - win32api.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') - - -def win_get_environment_variable(key, system=True): - prev_path = os.environ['PATH'] - try: - py = find_used_python() - if py: - py_path = to_native_path(py.expand_vars(py.activated_path)) - os.environ['PATH'] = os.environ['PATH'] + ';' + py_path - try: - import win32api - import win32con - if system: # Read globally from ALL USERS section. - folder = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment') - else: # Register locally from CURRENT USER section. - folder = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, 'Environment') - value = str(win32api.RegQueryValueEx(folder, key)[0]) - except Exception as e: - # PyWin32 is not available - read via os.environ. This has the drawback that expansion items such as %PROGRAMFILES% will have been expanded, so - # need to be precise not to set these back to system registry, or expansion items would be lost. - return os.environ[key] - except Exception as e: - if e[0] != 2: # 'The system cannot find the file specified.' - print('Failed to read environment variable ' + key + ':', file=sys.stderr) - print(str(e), file=sys.stderr) - try: - win32api.RegCloseKey(folder) - except Exception as e: - pass - os.environ['PATH'] = prev_path - return None - win32api.RegCloseKey(folder) - os.environ['PATH'] = prev_path - return value - - -def win_environment_variable_exists(key, system=True): - value = win_get_environment_variable(key, system) - return value is not None and len(value) > 0 - - -def win_get_active_environment_variable(key): - value = win_get_environment_variable(key, False) - if value is not None: - return value - return win_get_environment_variable(key, True) - - -def win_set_environment_variable(key, value, system=True): - if VERBOSE: print('set ' + str(key) + '=' + str(value) + ', in system=' + str(system), file=sys.stderr) - previous_value = win_get_environment_variable(key, system) - if previous_value == value: - if VERBOSE: print(' no need to set, since same value already exists.') - return # No need to elevate UAC for nothing to set the same value, skip. - - if not value: - try: - if system: - cmd = ['REG', 'DELETE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', '/V', key, '/f'] - else: - cmd = ['REG', 'DELETE', 'HKCU\\Environment', '/V', key, '/f'] - if VERBOSE: print(str(cmd)) - value = subprocess.call(cmd, stdout=subprocess.PIPE) - except Exception as e: - return - return - - try: - if system: - win_set_environment_variable_direct(key, value, system) - return - value = value.replace('%', '^%') # Escape % signs so that we don't expand references to environment variables. - if len(value) >= 1024: - print('ERROR! The new environment variable ' + key + ' is more than 1024 characters long! A value this long cannot be set via command line: please add the environment variable specified above to system environment manually via Control Panel.', file=sys.stderr) - sys.exit(1) - cmd = ['SETX', key, value] - if VERBOSE: print(str(cmd)) - retcode = subprocess.call(cmd, stdout=subprocess.PIPE) - if retcode is not 0: - print('ERROR! Failed to set environment variable ' + key + '=' + value + '. You may need to set it manually.', file=sys.stderr) - except Exception as e: - print('ERROR! Failed to set environment variable ' + key + '=' + value + ':', file=sys.stderr) - print(str(e), file=sys.stderr) - print('You may need to set it manually.', file=sys.stderr) - - -def win_delete_environment_variable(key, system=True): - if VERBOSE: print('win_delete_environment_variable(key=' + key + ', system=' + str(system) + ')') - win_set_environment_variable(key, None, system) - - -# Returns the absolute pathname to the given path inside the Emscripten SDK. -def sdk_path(path): - if os.path.isabs(path): - return path - else: - return to_unix_path(os.path.join(os.path.dirname(os.path.realpath(__file__)), path)) - - -# Modifies the given file in-place to contain '\r\n' line endings. -def file_to_crlf(filename): - text = open(filename, 'r').read() - text = text.replace('\r\n', '\n').replace('\n', '\r\n') - open(filename, 'wb').write(text) - - -# Modifies the given file in-place to contain '\n' line endings. -def file_to_lf(filename): - text = open(filename, 'r').read() - text = text.replace('\r\n', '\n') - open(filename, 'wb').write(text) - - -# Removes a single file, suppressing exceptions on failure. -def rmfile(filename): - if VERBOSE: print('rmfile(' + filename + ')') - try: - os.remove(filename) - except: - pass - - -def fix_lineendings(filename): - if WINDOWS: - file_to_crlf(filename) - else: - file_to_lf(filename) - - -# http://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python -def mkdir_p(path): - if VERBOSE: print('mkdir_p(' + path + ')') - if os.path.exists(path): - return - try: - os.makedirs(path) - except OSError as exc: # Python >2.5 - if exc.errno == errno.EEXIST and os.path.isdir(path): - pass - else: raise - - -def num_files_in_directory(path): - if not os.path.isdir(path): - return 0 - return len([name for name in os.listdir(path) if os.path.exists(os.path.join(path, name))]) - - -def run(cmd, cwd=None): - if VERBOSE: print('run(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')') - process = subprocess.Popen(cmd, cwd=cwd, env=os.environ.copy()) - process.communicate() - if process.returncode != 0: - print(str(cmd) + ' failed with error code ' + str(process.returncode) + '!') - return process.returncode - - -# http://pythonicprose.blogspot.fi/2009/10/python-extract-targz-archive.html -def untargz(source_filename, dest_dir, unpack_even_if_exists=False): - if VERBOSE: print('untargz(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')') - if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0: - print("File '" + source_filename + "' has already been unpacked, skipping.") - return True - print("Unpacking '" + source_filename + "' to '" + dest_dir + "'") - mkdir_p(dest_dir) - run(['tar', '-xvf' if VERBOSE else '-xf', sdk_path(source_filename), '--strip', '1'], cwd=dest_dir) - # tfile = tarfile.open(source_filename, 'r:gz') - # tfile.extractall(dest_dir) - return True - - -# On Windows, it is not possible to reference path names that are longer than ~260 characters, unless the path is referenced via a "\\?\" prefix. -# See https://msdn.microsoft.com/en-us/library/aa365247.aspx#maxpath and http://stackoverflow.com/questions/3555527/python-win32-filename-length-workaround -# In that mode, forward slashes cannot be used as delimiters. -def fix_potentially_long_windows_pathname(pathname): - if not WINDOWS: return pathname - # Test if emsdk calls fix_potentially_long_windows_pathname() with long relative paths (which is problematic) - if not os.path.isabs(pathname) and len(pathname) > 200: print('Warning: Seeing a relative path "' + pathname + '" which is dangerously long for being referenced as a short Windows path name. Refactor emsdk to be able to handle this!') - if pathname.startswith('\\\\?\\'): return pathname - return '\\\\?\\' + os.path.normpath(pathname.replace('/', '\\')) - - -# http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses -def unzip(source_filename, dest_dir, unpack_even_if_exists=False): - if VERBOSE: print('unzip(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')') - if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0: - print("File '" + source_filename + "' has already been unpacked, skipping.") - return True - print("Unpacking '" + source_filename + "' to '" + dest_dir + "'") - mkdir_p(dest_dir) - common_subdir = None - try: - with zipfile.ZipFile(source_filename) as zf: - # Implement '--strip 1' behavior to unzipping by testing if all the files in the zip reside in a common subdirectory, and if so, - # we move the output tree at the end of uncompression step. - for member in zf.infolist(): - words = member.filename.split('/') - if len(words) > 1: # If there is a directory component? - if common_subdir is None: - common_subdir = words[0] - elif common_subdir != words[0]: - common_subdir = None - break - else: - common_subdir = None - break - - unzip_to_dir = dest_dir - if common_subdir: - unzip_to_dir = os.path.join('/'.join(dest_dir.split('/')[:-1]), 'unzip_temp') - - # Now do the actual decompress. - for member in zf.infolist(): - zf.extract(member, fix_potentially_long_windows_pathname(unzip_to_dir)) - - # Move the extracted file to its final location without the base directory name, if we are stripping that away. - if common_subdir: - if not member.filename.startswith(common_subdir): - raise Exception('Unexpected filename "' + member.filename + '"!') - stripped_filename = '.' + member.filename[len(common_subdir):] - final_dst_filename = os.path.join(dest_dir, stripped_filename) - if stripped_filename.endswith('/'): # Directory? - d = fix_potentially_long_windows_pathname(final_dst_filename) - if not os.path.isdir(d): os.mkdir(d) - else: - tmp_dst_filename = os.path.join(unzip_to_dir, member.filename) - parent_dir = os.path.dirname(fix_potentially_long_windows_pathname(final_dst_filename)) - if parent_dir and not os.path.exists(parent_dir): - os.makedirs(parent_dir) - os.rename(fix_potentially_long_windows_pathname(tmp_dst_filename), fix_potentially_long_windows_pathname(final_dst_filename)) - - if common_subdir: - try: - remove_tree(unzip_to_dir) - except: - pass - except zipfile.BadZipfile as e: - print("Unzipping file '" + source_filename + "' failed due to reason: " + str(e) + "! Removing the corrupted zip file.") - rmfile(source_filename) - return False - except Exception as e: - print("Unzipping file '" + source_filename + "' failed due to reason: " + str(e)) - return False - - return True - - -# This function interprets whether the given string looks like a path to a directory instead of a file, without looking at the actual filesystem. -# 'a/b/c' points to directory, so does 'a/b/c/', but 'a/b/c.x' is parsed as a filename -def path_points_to_directory(path): - if path == '.': - return True - last_slash = max(path.rfind('/'), path.rfind('\\')) - last_dot = path.rfind('.') - no_suffix = last_dot < last_slash or last_dot == -1 - if no_suffix: - return True - suffix = path[last_dot:] - if suffix == '.exe' or suffix == '.zip' or suffix == '.txt': # Very simple logic for the only file suffixes used by emsdk downloader. Other suffixes, like 'clang-3.2' are treated as dirs. - return False - else: - return True - - -def get_content_length(download): - try: - meta = download.info() - if hasattr(meta, "getheaders") and hasattr(meta.getheaders, "Content-Length"): return int(meta.getheaders("Content-Length")[0]) - elif hasattr(download, "getheader") and download.getheader('Content-Length'): return int(download.getheader('Content-Length')) - elif hasattr(meta, "getheader") and meta.getheader('Content-Length'): return int(meta.getheader('Content-Length')) - except Exception: - pass - - return 0 - - -# On success, returns the filename on the disk pointing to the destination file that was produced -# On failure, returns None. -def download_file(url, dstpath, download_even_if_exists=False, filename_prefix=''): - if VERBOSE: print('download_file(url=' + url + ', dstpath=' + dstpath + ')') - file_name = filename_prefix + url.split('/')[-1] - if path_points_to_directory(dstpath): - file_name = os.path.join(dstpath, file_name) - else: - file_name = dstpath - - # Treat all relative destination paths as relative to the SDK root directory, not the current working directory. - file_name = sdk_path(file_name) - - if os.path.exists(file_name) and not download_even_if_exists: - print("File '" + file_name + "' already downloaded, skipping.") - return file_name - try: - u = urlopen(url) - mkdir_p(os.path.dirname(file_name)) - with open(file_name, 'wb') as f: - file_size = get_content_length(u) - if file_size > 0: print("Downloading: %s from %s, %s Bytes" % (file_name, url, file_size)) - else: print("Downloading: %s from %s" % (file_name, url)) - - file_size_dl = 0 - # Draw a progress bar 80 chars wide (in non-TTY mode) - progress_max = 80 - 4 - progress_shown = 0 - block_sz = 8192 - if not TTY_OUTPUT: - print(' [', end='') - while True: - buffer = u.read(block_sz) - if not buffer: - break - - file_size_dl += len(buffer) - f.write(buffer) - if file_size: - percent = file_size_dl * 100.0 / file_size - if TTY_OUTPUT: - status = r" %10d [%3.02f%%]" % (file_size_dl, percent) - print(status, end='\r') - else: - while progress_shown < progress_max * percent / 100: - print('-', end='') - sys.stdout.flush() - progress_shown += 1 - if not TTY_OUTPUT: - print(']') - except Exception as e: - print("Error downloading URL '" + url + "': " + str(e)) - rmfile(file_name) - return None - except KeyboardInterrupt as e: - print("Aborted by User, exiting") - rmfile(file_name) - sys.exit(1) - return file_name - - -def download_text_file(url, dstpath, download_even_if_exists=False, filename_prefix=''): - filename = download_file(url, dstpath, download_even_if_exists, filename_prefix) - fix_lineendings(os.path.join(emsdk_path(), filename)) - - -def run_get_output(cmd, cwd=None): - if VERBOSE: print('run_get_output(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')') - process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=os.environ.copy(), universal_newlines=True) - (stdout, stderr) = process.communicate() - return (process.returncode, stdout, stderr) - - -# must_succeed: If false, the search is performed silently without printing out errors if not found. Empty string is returned if git is not found. -# If true, the search is required to succeed, and the execution will terminate with sys.exit(1) if not found. -def GIT(must_succeed=True): - # The order in the following is important, and specifies the preferred order of using the git tools. - # Primarily use git from emsdk if installed. If not, use system git. - gits = ['git/1.9.4/bin/git.exe', which('git')] - for git in gits: - try: - (ret, stdout, stderr) = run_get_output([git, '--version']) - if ret == 0: - return git - except: - pass - if must_succeed: - if WINDOWS: - print("ERROR: git executable was not found. Please install it by typing 'emsdk install git-1.9.4', or alternatively by installing it manually from http://git-scm.com/downloads . If you install git manually, remember to add it to PATH") - elif OSX: - print("ERROR: git executable was not found. Please install git for this operation! This can be done from http://git-scm.com/ , or by installing XCode and then the XCode Command Line Tools (see http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools )") - elif LINUX: - print("ERROR: git executable was not found. Please install git for this operation! This can be probably be done using your package manager, see http://git-scm.com/book/en/Getting-Started-Installing-Git") - else: - print("ERROR: git executable was not found. Please install git for this operation!") - sys.exit(1) - return '' # Not found - - -def git_repo_version(repo_path): - returncode, stdout, stderr = run_get_output([GIT(), 'log', '-n', '1', '--pretty="%aD %H"'], cwd=repo_path) - if returncode == 0: - return stdout.strip() - else: - return "" - - -def git_clone(url, dstpath): - if VERBOSE: print('git_clone(url=' + url + ', dstpath=' + dstpath + ')') - if os.path.isdir(os.path.join(dstpath, '.git')): - print("Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping.") - return True - mkdir_p(dstpath) - git_clone_args = [] - if GIT_CLONE_SHALLOW: - git_clone_args += ['--depth', '1'] - return run([GIT(), 'clone'] + git_clone_args + [url, dstpath]) == 0 - - -def git_checkout_and_pull(repo_path, branch): - if VERBOSE: print('git_checkout_and_pull(repo_path=' + repo_path + ', branch=' + branch + ')') - ret = run([GIT(), 'fetch', 'origin'], repo_path) - if ret != 0: return False - try: - print("Fetching latest changes to the branch '" + branch + "' for '" + repo_path + "'...") - ret = run([GIT(), 'fetch', 'origin'], repo_path) - if ret != 0: return False -# run([GIT, 'checkout', '-b', branch, '--track', 'origin/'+branch], repo_path) - ret = run([GIT(), 'checkout', '--quiet', branch], repo_path) # this line assumes that the user has not gone and manually messed with the repo and added new remotes to ambiguate the checkout. - if ret != 0: return False - ret = run([GIT(), 'merge', '--ff-only', 'origin/' + branch], repo_path) # this line assumes that the user has not gone and made local changes to the repo - if ret != 0: return False - except: - print('git operation failed!') - return False - print("Successfully updated and checked out branch '" + branch + "' on repository '" + repo_path + "'") - print("Current repository version: " + git_repo_version(repo_path)) - return True - - -def git_clone_checkout_and_pull(url, dstpath, branch): - if VERBOSE: print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ')') - success = git_clone(url, dstpath) - if not success: - return False - success = git_checkout_and_pull(dstpath, branch) - return success - - -# Each tool can have its own build type, or it can be overridden on the command line. -def decide_cmake_build_type(tool): - global CMAKE_BUILD_TYPE_OVERRIDE - if CMAKE_BUILD_TYPE_OVERRIDE: - return CMAKE_BUILD_TYPE_OVERRIDE - else: - return tool.cmake_build_type - - -# The root directory of the build. -def fastcomp_build_dir(tool): - generator_suffix = '' - if CMAKE_GENERATOR == 'Visual Studio 10': generator_suffix = '_vs2010' - elif CMAKE_GENERATOR == 'Visual Studio 11': generator_suffix = '_vs2012' - elif CMAKE_GENERATOR == 'Visual Studio 12': generator_suffix = '_vs2013' - elif CMAKE_GENERATOR == 'Visual Studio 14': generator_suffix = '_vs2015' - elif CMAKE_GENERATOR == 'Visual Studio 15': generator_suffix = '_vs2017' - elif CMAKE_GENERATOR == 'MinGW Makefiles': generator_suffix = '_mingw' - - bitness_suffix = '_32' if tool.bitness == 32 else '_64' - - if hasattr(tool, 'git_branch'): - build_dir = 'build_' + tool.git_branch.replace(os.sep, '-') + generator_suffix + bitness_suffix - else: - build_dir = 'build_' + tool.version + generator_suffix + bitness_suffix - return build_dir - - -def exe_suffix(filename): - if WINDOWS and not filename.endswith('.exe'): return filename + '.exe' - else: return filename - - -# The directory where the binaries are produced. (relative to the installation root directory of the tool) -def fastcomp_build_bin_dir(tool): - build_dir = fastcomp_build_dir(tool) - if WINDOWS and 'Visual Studio' in CMAKE_GENERATOR: - old_llvm_bin_dir = os.path.join(build_dir, 'bin', decide_cmake_build_type(tool)) - - new_llvm_bin_dir = None - default_cmake_build_type = decide_cmake_build_type(tool) - cmake_build_types = [default_cmake_build_type, 'Release', 'RelWithDebInfo', 'MinSizeRel', 'Debug'] - for build_type in cmake_build_types: - d = os.path.join(build_dir, build_type, 'bin') - if os.path.isfile(os.path.join(tool.installation_path(), d, exe_suffix('clang'))): - new_llvm_bin_dir = d - break - - if new_llvm_bin_dir and os.path.exists(os.path.join(tool.installation_path(), new_llvm_bin_dir)): return new_llvm_bin_dir - elif os.path.exists(os.path.join(tool.installation_path(), old_llvm_bin_dir)): return old_llvm_bin_dir - return os.path.join(build_dir, default_cmake_build_type, 'bin') - else: - return os.path.join(build_dir, 'bin') - - -def build_env(generator): - build_env = os.environ.copy() - - # To work around a build issue with older Mac OS X builds, add -stdlib=libc++ to all builds. - # See https://groups.google.com/forum/#!topic/emscripten-discuss/5Or6QIzkqf0 - if OSX: build_env['CXXFLAGS'] = ((build_env['CXXFLAGS'] + ' ') if hasattr(build_env, 'CXXFLAGS') else '') + '-stdlib=libc++' - - elif 'Visual Studio 15' in generator: - path = vswhere(15) - build_env['VCTargetsPath'] = os.path.join(path, 'Common7\\IDE\\VC\\VCTargets') - - # CMake and VS2017 cl.exe needs to have mspdb140.dll et al. in its PATH. - vc_bin_paths = [vs_filewhere(path, 'amd64', 'cl.exe'), - vs_filewhere(path, 'x86', 'cl.exe')] - for path in vc_bin_paths: - if os.path.isdir(path): - build_env['PATH'] = build_env['PATH'] + ';' + path - - elif 'Visual Studio 14' in generator or 'Visual Studio 2015' in generator: - build_env['VCTargetsPath'] = os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/Microsoft.Cpp/v4.0/V140') - - # CMake and VS2015 cl.exe needs to have mspdb140.dll et al. in its PATH. - vc_bin_paths = [os.path.join(os.environ['ProgramFiles'], 'Microsoft Visual Studio 14.0\\VC\\bin'), - os.path.join(os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio 14.0\\VC\\bin')] - for path in vc_bin_paths: - if os.path.isdir(path): - build_env['PATH'] = build_env['PATH'] + ';' + path - - elif 'Visual Studio 12' in generator or 'Visual Studio 2013' in generator: - build_env['VCTargetsPath'] = os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/Microsoft.Cpp/v4.0/V120') - - return build_env - - -def get_generator_for_sln_file(sln_file): - contents = open(sln_file, 'r').read() - if '# Visual Studio 15' in contents: - return 'Visual Studio 15' - if '# Visual Studio Express 2015' in contents or '# Visual Studio 2015' in contents or '# Visual Studio 14' in contents: - return 'Visual Studio 14' - if '# Visual Studio Express 2013' in contents or '# Visual Studio 2013' in contents or '# Visual Studio 12' in contents: - return 'Visual Studio 12' - raise Exception('Unknown generator used to build solution file ' + sln_file) - - -def find_msbuild(sln_file): - # The following logic attempts to find a Visual Studio version specific MSBuild.exe from a list of known locations. This logic - # exists because it was detected that when multiple Visual Studio versions exist (VS2013 & VS2015), their MSBuild.exes might not - # be able to drive a build proper. This search is messy, and perhaps in VS >= 2017 or similar none of this logic would be needed. - # Ideally would be able to do "cmake --build path/to/cmake/build/directory --config Debug|RelWithDebInfo|MinSizeRel|Release" across - # all platforms, but around VS2013 era this did not work. This could be reattempted when support for VS 2015 is dropped. - search_paths_vs2015 = [os.path.join(os.environ['ProgramFiles'], 'MSBuild/14.0/Bin/amd64'), - os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/14.0/Bin/amd64'), - os.path.join(os.environ['ProgramFiles'], 'MSBuild/14.0/Bin'), - os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/14.0/Bin')] - search_paths_vs2013 = [os.path.join(os.environ['ProgramFiles'], 'MSBuild/12.0/Bin/amd64'), - os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/12.0/Bin/amd64'), - os.path.join(os.environ['ProgramFiles'], 'MSBuild/12.0/Bin'), - os.path.join(os.environ['ProgramFiles(x86)'], 'MSBuild/12.0/Bin')] - search_paths_old = [os.path.join(os.environ["WINDIR"], 'Microsoft.NET/Framework/v4.0.30319')] - generator = get_generator_for_sln_file(sln_file) - if VERBOSE: - print('find_msbuild looking for generator ' + str(generator)) - if generator == 'Visual Studio 15': - path = vswhere(15) - search_paths = [os.path.join(path, 'MSBuild/15.0/Bin/amd64'), - os.path.join(path, 'MSBuild/15.0/Bin')] - elif generator == 'Visual Studio 14': - search_paths = search_paths_vs2015 - elif generator == 'Visual Studio 12': - search_paths = search_paths_vs2013 + search_paths_old - else: - raise Exception('Unknown generator!') - - for path in search_paths: - p = os.path.join(path, 'MSBuild.exe') - if VERBOSE: - print('Searching for MSBuild.exe: ' + p) - if os.path.isfile(p): return p - if VERBOSE: - print('MSBuild.exe in PATH? ' + str(which('MSBuild.exe'))) - return which('MSBuild.exe') # Last fallback, try any MSBuild from PATH (might not be compatible, but best effort) - - -def make_build(build_root, build_type, build_target_platform='x64'): - if VERBOSE: print('make_build(build_root=' + build_root + ', build_type=' + build_type + ', build_target_platform=' + build_target_platform + ')') - global CPU_CORES - if CPU_CORES > 1: - print('Performing a parallel build with ' + str(CPU_CORES) + ' cores.') - else: - print('Performing a singlethreaded build.') - - generator_to_use = CMAKE_GENERATOR - - if WINDOWS: - if 'Visual Studio' in CMAKE_GENERATOR: - solution_name = str(subprocess.check_output(['dir', '/b', '*.sln'], shell=True, cwd=build_root).decode('utf-8').strip()) - generator_to_use = get_generator_for_sln_file(os.path.join(build_root, solution_name)) - # Disabled for now: Don't pass /maxcpucount argument to msbuild, since it looks like when building, msbuild already automatically spawns the full amount of logical - # cores the system has, and passing the number of logical cores here has been observed to give a quadratic N*N explosion on the number of spawned processes - # (e.g. on a Core i7 5960X with 16 logical cores, it would spawn 16*16=256 cl.exe processes, which would start crashing when running out of system memory) - # make = [find_msbuild(os.path.join(build_root, solution_name)), '/maxcpucount:' + str(CPU_CORES), '/t:Build', '/p:Configuration=' + build_type, '/nologo', '/verbosity:minimal', solution_name] - make = [find_msbuild(os.path.join(build_root, solution_name)), '/t:Build', '/p:Configuration=' + build_type, '/p:Platform=' + build_target_platform, '/nologo', '/verbosity:minimal', solution_name] - else: - make = ['mingw32-make', '-j' + str(CPU_CORES)] - else: - make = ['make', '-j' + str(CPU_CORES)] - - # Build - try: - print('Running build: ' + str(make)) - ret = subprocess.check_call(make, cwd=build_root, env=build_env(generator_to_use)) - if ret != 0: - print('Build failed with exit code ' + ret + '!', file=sys.stderr) - print('Working directory: ' + build_root, file=sys.stderr) - return False - except Exception as e: - print('Build failed due to exception!', file=sys.stderr) - print('Working directory: ' + build_root, file=sys.stderr) - print(str(e), file=sys.stderr) - return False - - return True - - -def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_args=[]): - if VERBOSE: print('cmake_configure(generator=' + str(generator) + ', build_root=' + str(build_root) + ', src_root=' + str(src_root) + ', build_type=' + str(build_type) + ', extra_cmake_args=' + str(extra_cmake_args) + ')') - # Configure - if not os.path.isdir(build_root): os.mkdir(build_root) # Create build output directory if it doesn't yet exist. - try: - if generator: generator = ['-G', generator] - else: generator = [] - cmdline = ['cmake'] + generator + ['-DCMAKE_BUILD_TYPE=' + build_type, '-DPYTHON_EXECUTABLE=' + sys.executable] + extra_cmake_args + [src_root] - print('Running CMake: ' + str(cmdline)) - - def quote_parens(x): - if ' ' in x: - return '"' + x.replace('"', '\\"') + '"' - else: - return x - - open(os.path.join(build_root, 'recmake.' + ('bat' if WINDOWS else 'sh')), 'w').write(' '.join(map(quote_parens, cmdline))) # Create a file 'recmake.bat/sh' in the build root that user can call to manually recmake the build tree with the previous build params - ret = subprocess.check_call(cmdline, cwd=build_root, env=build_env(CMAKE_GENERATOR)) - if ret != 0: - print('CMake invocation failed with exit code ' + ret + '!', file=sys.stderr) - print('Working directory: ' + build_root, file=sys.stderr) - return False - except OSError as e: - if e.errno == errno.ENOENT: - print(str(e), file=sys.stderr) - print('Could not run CMake, perhaps it has not been installed?', file=sys.stderr) - if WINDOWS: - print('Installing this package requires CMake. Get it from http://www.cmake.org/', file=sys.stderr) - elif LINUX: - print('Installing this package requires CMake. Get it via your system package manager (e.g. sudo apt-get install cmake), or from http://www.cmake.org/', file=sys.stderr) - elif OSX: - print('Installing this package requires CMake. Get it via a OSX package manager (Homebrew: "brew install cmake", or MacPorts: "sudo port install cmake"), or from http://www.cmake.org/', file=sys.stderr) - return False - raise - except Exception as e: - print('CMake invocation failed due to exception!', file=sys.stderr) - print('Working directory: ' + build_root, file=sys.stderr) - print(str(e), file=sys.stderr) - return False - - return True - - -def xcode_sdk_version(): - try: - return subprocess.check_output(['xcrun', '--show-sdk-version']).strip().split('.') - except: - return subprocess.checkplatform.mac_ver()[0].split('.') - - -def build_llvm_tool(tool): - if VERBOSE: print('build_llvm_tool(' + str(tool) + ')') - fastcomp_root = tool.installation_path() - fastcomp_src_root = os.path.join(fastcomp_root, 'src') - if hasattr(tool, 'git_branch'): # Does this tool want to be git cloned from github? - success = git_clone_checkout_and_pull(tool.download_url(), fastcomp_src_root, tool.git_branch) - if not success: return False - clang_root = os.path.join(fastcomp_src_root, 'tools/clang') - success = git_clone_checkout_and_pull(tool.clang_url, clang_root, tool.git_branch) - if not success: return False - if hasattr(tool, 'lld_url'): - lld_root = os.path.join(fastcomp_src_root, 'tools/lld') - success = git_clone_checkout_and_pull(tool.lld_url, lld_root, tool.git_branch) - if not success: return False - else: # Not a git cloned tool, so instead download from git tagged releases - success = download_and_unzip(tool.download_url(), fastcomp_src_root, filename_prefix='llvm-e') - if not success: return False - success = download_and_unzip(tool.windows_clang_url if WINDOWS else tool.unix_clang_url, os.path.join(fastcomp_src_root, 'tools/clang'), filename_prefix='clang-e') - if not success: return False - - cmake_generator = CMAKE_GENERATOR - if 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: - cmake_generator += ' Win64' - - build_dir = fastcomp_build_dir(tool) - build_root = os.path.join(fastcomp_root, build_dir) - - build_type = decide_cmake_build_type(tool) - - # Configure - global BUILD_FOR_TESTING, ENABLE_LLVM_ASSERTIONS - tests_arg = 'ON' if BUILD_FOR_TESTING else 'OFF' - - enable_assertions = ENABLE_LLVM_ASSERTIONS.lower() == 'on' or (ENABLE_LLVM_ASSERTIONS == 'auto' and build_type.lower() != 'release' and build_type.lower() != 'minsizerel') - - only_supports_wasm = hasattr(tool, 'only_supports_wasm') - targets_to_build = 'X86' - if not only_supports_wasm: - targets_to_build += ';JSBackend' - args = ['-DLLVM_TARGETS_TO_BUILD=' + targets_to_build, '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DCLANG_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_TESTS=' + tests_arg, '-DCLANG_INCLUDE_TESTS=' + tests_arg, '-DLLVM_ENABLE_ASSERTIONS=' + ('ON' if enable_assertions else 'OFF')] - if ENABLE_WASM or only_supports_wasm: - args += ['-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly'] - if os.environ.get('LLVM_CMAKE_ARGS'): - extra_args = os.environ['LLVM_CMAKE_ARGS'].split(',') - print('Passing the following extra arguments to LLVM CMake configuration: ' + str(extra_args)) - args += extra_args - - # MacOS < 10.13 workaround for LLVM build bug https://github.com/kripken/emscripten/issues/5418: - # specify HAVE_FUTIMENS=0 in the build if building with target SDK that is older than 10.13. - if OSX and (not os.environ.get('LLVM_CMAKE_ARGS') or 'HAVE_FUTIMENS' not in os.environ.get('LLVM_CMAKE_ARGS')) and xcode_sdk_version() < [10, 13]: - print('Passing -DHAVE_FUTIMENS=0 to LLVM CMake configure to workaround https://github.com/kripken/emscripten/issues/5418. Please update to macOS 10.13 or newer') - args += ['-DHAVE_FUTIMENS=0'] - - success = cmake_configure(cmake_generator, build_root, fastcomp_src_root, build_type, args) - if not success: return False - - # Make - success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') - return success - - -# Emscripten asm.js optimizer build scripts: -def optimizer_build_root(tool): - build_root = tool.installation_path().strip() - if build_root.endswith('/') or build_root.endswith('\\'): build_root = build_root[:-1] - generator_prefix = cmake_generator_prefix() - build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_optimizer' - return build_root - - -def uninstall_optimizer(tool): - if VERBOSE: print('uninstall_optimizer(' + str(tool) + ')') - build_root = optimizer_build_root(tool) - print("Deleting path '" + build_root + "'") - try: - remove_tree(build_root) - os.remove(build_root) - except: - pass - - -def is_optimizer_installed(tool): - build_root = optimizer_build_root(tool) - return os.path.exists(build_root) - - -def build_optimizer_tool(tool): - if VERBOSE: print('build_optimizer_tool(' + str(tool) + ')') - src_root = os.path.join(tool.installation_path(), 'tools', 'optimizer') - build_root = optimizer_build_root(tool) - build_type = decide_cmake_build_type(tool) - - # Configure - cmake_generator = CMAKE_GENERATOR - if 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: - cmake_generator += ' Win64' - success = cmake_configure(cmake_generator, build_root, src_root, build_type) - if not success: return False - - # Make - success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') - return success - - -# Binaryen build scripts: -def binaryen_build_root(tool): - build_root = tool.installation_path().strip() - if build_root.endswith('/') or build_root.endswith('\\'): build_root = build_root[:-1] - generator_prefix = cmake_generator_prefix() - build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_binaryen' - return build_root - - -def uninstall_binaryen(tool): - if VERBOSE: print('uninstall_binaryen(' + str(tool) + ')') - build_root = binaryen_build_root(tool) - print("Deleting path '" + build_root + "'") - try: - remove_tree(build_root) - os.remove(build_root) - except: - pass - - -def is_binaryen_installed(tool): - build_root = binaryen_build_root(tool) - return os.path.exists(build_root) - - -def build_binaryen_tool(tool): - if VERBOSE: print('build_binaryen_tool(' + str(tool) + ')') - src_root = tool.installation_path() - build_root = binaryen_build_root(tool) - build_type = decide_cmake_build_type(tool) - - # Configure - args = [] - - cmake_generator = CMAKE_GENERATOR - if 'Visual Studio' in CMAKE_GENERATOR: - if tool.bitness == 64: cmake_generator += ' Win64' - if BUILD_FOR_TESTING: args += ['-DRUN_STATIC_ANALYZER=1'] - - success = cmake_configure(cmake_generator, build_root, src_root, build_type, args) - if not success: return False - - # Make - success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') - - # Deploy scripts needed from source repository to build directory - remove_tree(os.path.join(build_root, 'scripts')) - shutil.copytree(os.path.join(src_root, 'scripts'), os.path.join(build_root, 'scripts')) - remove_tree(os.path.join(build_root, 'src', 'js')) - shutil.copytree(os.path.join(src_root, 'src', 'js'), os.path.join(build_root, 'src', 'js')) - - return success - - -def download_and_unzip(zipfile, dest_dir, download_even_if_exists=False, filename_prefix=''): - if VERBOSE: print('download_and_unzip(zipfile=' + zipfile + ', dest_dir=' + dest_dir + ')') - if not download_even_if_exists and num_files_in_directory(dest_dir) > 0: - print("The contents of file '" + zipfile + "' already exist in destination '" + dest_dir + "', skipping.") - return True - dst_file = download_file(urljoin(emsdk_packages_url, zipfile), 'zips/', download_even_if_exists, filename_prefix) - if not dst_file: - return False - if zipfile.endswith('.zip'): - return unzip(dst_file, dest_dir, unpack_even_if_exists=download_even_if_exists) - else: - return untargz(dst_file, dest_dir, unpack_even_if_exists=download_even_if_exists) - - -def to_native_path(p): - if WINDOWS and not MSYS: - return to_unix_path(p).replace('/', '\\') - else: - return to_unix_path(p) - - -# Finds and returns a list of the directories that need to be added to PATH for the given set of tools. -def get_required_path(active_tools): - path_add = [to_native_path(emsdk_path())] - for tool in active_tools: - if hasattr(tool, 'activated_path'): - path_add += [to_native_path(tool.expand_vars(tool.activated_path))] - return path_add - - -# Returns the absolute path to the file '.emscripten' for the current user on this system. -def dot_emscripten_path(): - return os.path.join(emscripten_config_directory, ".emscripten") - - -dot_emscripten = {} - - -def parse_key_value(line): - if not line: - return ('', '') - eq = line.find('=') - if eq != -1: - key = line[0:eq].strip() - value = line[eq + 1:].strip() - return (key, value) - else: - return (key, '') - - -def load_dot_emscripten(): - global dot_emscripten - dot_emscripten = {} - lines = [] - try: - lines = open(dot_emscripten_path(), "r").read().split('\n') - except: - pass - for line in lines: - try: - (key, value) = parse_key_value(line) - if value != '': - dot_emscripten[key] = value -# print("Got '" + key + "' = '" + value + "'") - except: - pass - - -def generate_dot_emscripten(active_tools): - global emscripten_config_directory - if emscripten_config_directory == emsdk_path(): - temp_dir = sdk_path('tmp') - mkdir_p(temp_dir) - embedded = True - else: - temp_dir = tempfile.gettempdir().replace('\\', '/') - embedded = False - - has_spidermonkey = False - has_node = False - - cfg = 'import os\n' - - if embedded: - cfg += "emsdk_path=os.path.dirname(os.environ.get('EM_CONFIG')).replace('\\\\', '/')\n" - - # Different tools may provide the same activated configs; the latest to be - # activated is the relevant one. - activated_keys_in_order = [] - activated_key_values = {} - - for tool in active_tools: - tool_cfg = tool.activated_config() - if tool_cfg: - for specific_cfg in tool_cfg.split(';'): - name, value = specific_cfg.split('=') - if name not in activated_key_values: - activated_keys_in_order.append(name) - activated_key_values[name] = value - - for name in activated_keys_in_order: - if name == 'SPIDERMONKEY_ENGINE': - has_spidermonkey = True - if name == 'NODE_JS': - has_node = True - cfg += name + ' = ' + activated_key_values[name] + '\n' - - # These two vars must always be defined, even though they might not exist. - if not has_spidermonkey: cfg += "SPIDERMONKEY_ENGINE = ''\n" - if not has_node: - node_fallback = which('nodejs') - if not node_fallback: - node_fallback = 'node' - cfg += "NODE_JS = '" + node_fallback + "'\n" - - cfg += '''V8_ENGINE = '' -TEMP_DIR = ''' + "'" + temp_dir + "'" + ''' -COMPILER_ENGINE = NODE_JS -JS_ENGINES = [NODE_JS] -''' - - if embedded: - cfg = cfg.replace(emscripten_config_directory, "' + emsdk_path + '") - - with open(dot_emscripten_path(), "w") as text_file: text_file.write(cfg) - - # Clear old cached emscripten content. - try: - remove_tree(os.path.join(emscripten_config_directory, ".emscripten_cache")) - os.remove(os.path.join(emscripten_config_directory, ".emscripten_sanity")) - os.remove(os.path.join(emscripten_config_directory, ".emscripten_cache__last_clear")) - except: - pass - - print("The Emscripten configuration file " + os.path.normpath(dot_emscripten_path()) + " has been rewritten with the following contents:") - print('') - print(cfg.strip()) - print('') - - path_add = get_required_path(active_tools) - if not WINDOWS: - emsdk_env = os.path.relpath(sdk_path('emsdk_env.sh')) - if '/' not in emsdk_env: - emsdk_env = './emsdk_env.sh' - print("To conveniently access the selected set of tools from the command line, consider adding the following directories to PATH, or call 'source " + emsdk_env + "' to do this for you.") - print('') - print(' ' + ENVPATH_SEPARATOR.join(path_add)) - - -def find_msbuild_dir(): - if 'ProgramFiles' in os.environ and os.environ['ProgramFiles']: - program_files = os.environ['ProgramFiles'] - else: - program_files = 'C:/Program Files' - if 'ProgramFiles(x86)' in os.environ and os.environ['ProgramFiles(x86)']: - program_files_x86 = os.environ['ProgramFiles(x86)'] - else: - program_files_x86 = 'C:/Program Files (x86)' - MSBUILDX86_DIR = os.path.join(program_files_x86, "MSBuild/Microsoft.Cpp/v4.0/Platforms") - MSBUILD_DIR = os.path.join(program_files, "MSBuild/Microsoft.Cpp/v4.0/Platforms") - if os.path.exists(MSBUILDX86_DIR): - return MSBUILDX86_DIR - elif os.path.exists(MSBUILD_DIR): - return MSBUILD_DIR - else: - return '' # No MSbuild installed. - - -def get_installed_vstool_version(installed_path): - try: - return open(installed_path + "/version.txt", "r").read() - except: - return None - - -class Tool(object): - def __init__(self, data): - # Convert the dictionary representation of the tool in 'data' to members of this class for convenience. - for key in data: - if sys.version_info < (3,) and isinstance(data[key], unicode): - setattr(self, key, data[key].encode('Latin-1')) - else: - setattr(self, key, data[key]) - - # Cache the name ID of this Tool (these are read very often) - self.name = self.id + '-' + self.version - if hasattr(self, 'bitness'): - self.name += '-' + str(self.bitness) + 'bit' - - def __str__(self): - return self.name - - def __repr__(self): - return self.name - - def expand_vars(self, str): - if WINDOWS and '%MSBuildPlatformsDir%' in str: - str = str.replace('%MSBuildPlatformsDir%', find_msbuild_dir()) - if '%cmake_build_type_on_win%' in str: - str = str.replace('%cmake_build_type_on_win%', (decide_cmake_build_type(self) + '/') if WINDOWS else '') - if '%installation_dir%' in str: - str = str.replace('%installation_dir%', sdk_path(self.installation_dir())) - if '%generator_prefix%' in str: - str = str.replace('%generator_prefix%', cmake_generator_prefix()) - str = str.replace('%.exe%', '.exe' if WINDOWS else '') - if '%fastcomp_build_dir%' in str: - str = str.replace('%fastcomp_build_dir%', fastcomp_build_dir(self)) - if '%fastcomp_build_bin_dir%' in str: - str = str.replace('%fastcomp_build_bin_dir%', fastcomp_build_bin_dir(self)) - return str - - # Return true if this tool requires building from source, and false if this is a precompiled tool. - def needs_compilation(self): - if hasattr(self, 'cmake_build_type'): return True - - if hasattr(self, 'uses'): - for tool_name in self.uses: - tool = find_tool(tool_name) - if not tool: - if VERBOSE: print('Tool ' + str(self) + ' depends on ' + tool_name + ' which does not exist!') - continue - if tool.needs_compilation(): return True - - return False - - # Specifies the target path where this tool will be installed to. This could either be a directory or a filename (e.g. in case of node.js) - def installation_path(self): - if WINDOWS and hasattr(self, 'windows_install_path'): - pth = self.expand_vars(self.windows_install_path) - return sdk_path(pth) - if hasattr(self, 'install_path'): - pth = self.expand_vars(self.install_path) - return sdk_path(pth) - p = self.version - if hasattr(self, 'bitness') and (not hasattr(self, 'append_bitness') or self.append_bitness): - p += '_' + str(self.bitness) + 'bit' - return sdk_path(os.path.join(self.id, p)) - - # Specifies the target directory this tool will be installed to. - def installation_dir(self): - dir = self.installation_path() - if path_points_to_directory(dir): - return dir - else: - return os.path.dirname(dir) - - # Returns the configuration item that needs to be added to .emscripten to make this Tool active for the current user. - def activated_config(self): - if hasattr(self, 'activated_cfg'): - return to_unix_path(self.expand_vars(self.activated_cfg)) - else: - return '' - - def activated_environment(self): - if hasattr(self, 'activated_env'): - return self.expand_vars(self.activated_env).split(';') - else: - return [] - - def compatible_with_this_os(self): - if hasattr(self, 'os'): - if self.os == 'all': - return True - if (WINDOWS and 'win' in self.os) or (LINUX and ('linux' in self.os or 'unix' in self.os)) or (OSX and ('osx' in self.os or 'unix' in self.os)): - return True - else: - return False - else: - if not hasattr(self, 'osx_url') and not hasattr(self, 'windows_url') and not hasattr(self, 'unix_url') and not hasattr(self, 'linux_url'): - return True - - if OSX and hasattr(self, 'osx_url'): - return True - - if LINUX and hasattr(self, 'linux_url'): - return True - - if WINDOWS and (hasattr(self, 'windows_url') or hasattr(self, 'windows_install_path')): - return True - - if (LINUX or OSX) and hasattr(self, 'unix_url'): - return True - - return hasattr(self, 'url') - - def is_installed(self): - # If this tool/sdk depends on other tools, require that all dependencies are installed for this tool to count as being installed. - if hasattr(self, 'uses'): - for tool_name in self.uses: - tool = find_tool(tool_name) - if tool is None: - print("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!") - return False - if not tool.is_installed(): - return False - - if self.download_url() is not None: - # For e.g. fastcomp clang from git repo, the activated PATH is the directory where the compiler is built to, and installation_path is - # the directory where the source tree exists. To distinguish between multiple packages sharing the same source - # (clang-master-32bit, clang-master-64bit, clang-incoming-32bit and clang-incoming-64bit each share the same git repo), require - # that in addition to the installation directory, each item in the activated PATH must exist. - activated_path = self.expand_vars(self.activated_path).split(';') if hasattr(self, 'activated_path') else [self.installation_path()] - - def each_path_exists(pathlist): - for path in pathlist: - if not os.path.exists(path): return False - return True - - content_exists = os.path.exists(self.installation_path()) and each_path_exists(activated_path) and (os.path.isfile(self.installation_path()) or num_files_in_directory(self.installation_path()) > 0) - - if self.id == 'vs-tool': # vs-tool is a special tool since all versions must be installed to the same dir, so dir name will not differentiate the version. - return content_exists and get_installed_vstool_version(self.installation_path()) == self.version - elif hasattr(self, 'custom_is_installed_script'): - if self.custom_is_installed_script == 'is_optimizer_installed': return is_optimizer_installed(self) - elif self.custom_is_installed_script == 'is_binaryen_installed': return is_binaryen_installed(self) - else: raise Exception('Unknown custom_is_installed_script directive "' + self.custom_is_installed_script + '"!') - else: - return content_exists - else: - return True # This tool does not contain downloadable elements, so it is installed by default. - - def is_active(self): - if not self.is_installed(): - return False - - if self.id == 'vs-tool': - return True # vs-tool is a special tool since all versions must be installed to the same dir, which means that if this tool is installed, it is also active. - - # All dependencies of this tool must be active as well. - deps = self.dependencies() - for tool in deps: - if not tool.is_active(): - return False - - activated_cfg = self.activated_config() - if activated_cfg == '': - return len(deps) > 0 - - activated_cfg = activated_cfg.split(';') - for cfg in activated_cfg: - cfg = cfg.strip() - (key, value) = parse_key_value(cfg) - if key not in dot_emscripten: - if VERBOSE: print(str(self) + ' is not active, because key="' + key + '" does not exist in .emscripten') - return False - - # If running in embedded mode, all paths are stored dynamically relative to the emsdk root, so normalize those first. - dot_emscripten_key = dot_emscripten[key].replace("' + emsdk_path + '", emsdk_path()) - if dot_emscripten_key != value: - if VERBOSE: print(str(self) + ' is not active, because key="' + key + '" has value "' + dot_emscripten_key + '" but should have value "' + value + '"') - return False - return True - - # Returns true if the system environment variables requires by this tool are currently active. - def is_env_active(self): - envs = self.activated_environment() - for env in envs: - key, value = parse_key_value(env) - if key not in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value): - if VERBOSE: print(str(self) + ' is not active, because environment variable key="' + key + '" has value "' + str(os.getenv(key)) + '" but should have value "' + value + '"') - return False - - if hasattr(self, 'activated_path'): - path = self.expand_vars(self.activated_path).replace('\\', '/') - path = path.split(ENVPATH_SEPARATOR) - for p in path: - path_items = os.environ['PATH'].replace('\\', '/').split(ENVPATH_SEPARATOR) - if not normalized_contains(path_items, p): - if VERBOSE: print(str(self) + ' is not active, because environment variable PATH item "' + p + '" is not present (PATH=' + os.environ['PATH'] + ')') - return False - return True - - def win_activate_env_vars(self, permanently_activate): - if WINDOWS: - envs = self.activated_environment() - for env in envs: - key, value = parse_key_value(env) - - if permanently_activate: - win_delete_environment_variable(key, False) # If there is an env var for the LOCAL USER with same name, it will hide the system var, so must remove that first. - - win_set_environment_variable(key, value, permanently_activate) - - # If this tool can be installed on this system, this function returns True. - # Otherwise, this function returns a string that describes the reason why this tool is not available. - def can_be_installed(self): - if self.id == 'vs-tool': - msbuild_dir = find_msbuild_dir() - if len(msbuild_dir) > 0: - return True - else: - return "Visual Studio 2010 was not found!" - else: - return True - - def download_url(self): - if WINDOWS and hasattr(self, 'windows_url'): - return self.windows_url - elif OSX and hasattr(self, 'osx_url'): - return self.osx_url - elif LINUX and hasattr(self, 'linux_url'): - return self.linux_url - elif (OSX or LINUX) and hasattr(self, 'unix_url'): - return self.unix_url - elif hasattr(self, 'url'): - return self.url - else: - return None - - def install(self): - if not self.can_be_installed(): - print("The tool '" + str(self) + "' is not available due to the reason: " + self.can_be_installed()) - return False - - if self.id == 'sdk': - print("Installing SDK '" + str(self) + "'..") - for tool_name in self.uses: - tool = find_tool(tool_name) - if tool is None: - print("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!") - success = tool.install() - if not success: - return False - print("Done installing SDK '" + str(self) + "'.") - return True - else: - print("Installing tool '" + str(self) + "'..") - url = self.download_url() - - if hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_fastcomp': - success = build_llvm_tool(self) - elif hasattr(self, 'git_branch'): - success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch) - elif url.endswith('zip') or url.endswith('.tar') or url.endswith('.gz') or url.endswith('.xz') or url.endswith('.tbz2'): - download_even_if_exists = (self.id == 'vs-tool') - filename_prefix = getattr(self, 'zipfile_prefix', '') - success = download_and_unzip(url, self.installation_path(), download_even_if_exists=download_even_if_exists, filename_prefix=filename_prefix) - else: - dst_file = download_file(urljoin(emsdk_packages_url, self.download_url()), self.installation_path()) - if dst_file: success = True - else: success = False - - if success: - if hasattr(self, 'custom_install_script'): - if self.custom_install_script == 'build_optimizer': success = build_optimizer_tool(self) - elif self.custom_install_script == 'build_fastcomp': pass # 'build_fastcomp' is a special one that does the download on its own, others do the download manually. - elif self.custom_install_script == 'build_binaryen': success = build_binaryen_tool(self) - else: raise Exception('Unknown custom_install_script command "' + self.custom_install_script + '"!') - - # Install an emscripten-version.txt file if told to. - if hasattr(self, 'emscripten_releases_hash'): - emscripten_version_file_path = os.path.join(to_native_path(self.expand_vars(self.activated_path)), 'emscripten-version.txt') - open(emscripten_version_file_path, 'w').write('"%s"' % get_emscripten_release_version(self.emscripten_releases_hash)) - - if not success: - print("Installation failed!") - return False - print("Done installing tool '" + str(self) + "'.") - - # Sanity check that the installation succeeded, and if so, remove unneeded leftover installation files. - if self.is_installed(): - self.cleanup_temp_install_files() - else: - print("Warning: The installation of '" + str(self) + "' seems to have failed, but no error was detected. Either something went wrong with the installation, or this may indicate an internal emsdk error.") - return True - - def cleanup_temp_install_files(self): - url = self.download_url() - if url.endswith('zip') or url.endswith('.tar') or url.endswith('.gz'): - file_name = url.split('/')[-1] - tempzipfile = os.path.join('zips/', file_name) - if VERBOSE: print("Deleting temporary zip file " + tempzipfile) - rmfile(tempzipfile) - - def uninstall(self): - if not self.is_installed(): - print("Tool '" + str(self) + "' was not installed. No need to uninstall.") - return - print("Uninstalling tool '" + str(self) + "'..") - if hasattr(self, 'custom_uninstall_script'): - if self.custom_uninstall_script == 'uninstall_optimizer': - uninstall_optimizer(self) - elif self.custom_uninstall_script == 'uninstall_binaryen': - uninstall_binaryen(self) - else: - raise Exception('Unknown custom_uninstall_script directive "' + self.custom_uninstall_script + '"!') - try: - print("Deleting path '" + self.installation_path() + "'") - remove_tree(self.installation_path()) - os.remove(self.installation_path()) - except: - pass - print("Done uninstalling '" + str(self) + "'.") - - def dependencies(self): - if not hasattr(self, 'uses'): - return [] - deps = [] - - for tool_name in self.uses: - tool = find_tool(tool_name) - if tool: - deps += [tool] - return deps - - def recursive_dependencies(self): - if not hasattr(self, 'uses'): - return [] - deps = [] - for tool_name in self.uses: - tool = find_tool(tool_name) - if tool: - deps += [tool] - deps += tool.recursive_dependencies() - return deps - - -# A global registry of all known Emscripten SDK tools available in the SDK manifest. -tools = [] -tools_map = {} - - -def add_tool(tool): - tool.is_sdk = False - tools.append(tool) - if find_tool(str(tool)): raise Exception('Duplicate tool ' + str(tool) + '! Existing:\n{' + ', '.join("%s: %s" % item for item in vars(find_tool(str(tool))).items()) + '}, New:\n{' + ', '.join("%s: %s" % item for item in vars(tool).items()) + '}') - tools_map[str(tool)] = tool - - -# A global registry of all known SDK toolsets. -sdks = [] -sdks_map = {} - - -def add_sdk(sdk): - sdk.is_sdk = True - sdks.append(sdk) - if find_sdk(str(sdk)): raise Exception('Duplicate sdk ' + str(sdk) + '! Existing:\n{' + ', '.join("%s: %s" % item for item in vars(find_sdk(str(sdk))).items()) + '}, New:\n{' + ', '.join("%s: %s" % item for item in vars(sdk).items()) + '}') - sdks_map[str(sdk)] = sdk - - -# N.B. In both tools and sdks list above, we take the convention that the newest items are at the back of the list (ascending chronological order) - -def find_tool(name): - if name in tools_map: return tools_map[name] - return None - - -def find_sdk(name): - if name in sdks_map: return sdks_map[name] - return None - - -def is_os_64bit(): # http://stackoverflow.com/questions/2208828/detect-64bit-os-windows-in-python - return platform.machine().endswith('64') - - -def find_latest_32bit_sdk(): - for sdk in reversed(sdks): # Newest SDK is always at the end of the list. - if not hasattr(sdk, 'bitness') or sdk.bitness == 32: # If no 'bitness' field, it means the SDK is universal. - if 'nightly' not in sdk.version: - return sdk - return None - - -def find_latest_64bit_sdk(): - for sdk in reversed(sdks): # Newest SDK is always at the end of the list. - if not hasattr(sdk, 'bitness') or sdk.bitness == 64: # If no 'bitness' field, it means the SDK is universal. - if 'nightly' not in sdk.version: - return sdk - return None - - -def find_latest_sdk(): - if is_os_64bit(): - return find_latest_64bit_sdk() - else: - return find_latest_32bit_sdk() - - -def find_latest_nightly_32bit_sdk(): - for sdk in reversed(sdks): # Newest SDK is always at the end of the list. - if not hasattr(sdk, 'bitness') or sdk.bitness == 32: # If no 'bitness' field, it means the SDK is universal. - if 'nightly' in sdk.version: - return sdk - return None - - -def find_latest_nightly_64bit_sdk(): - for sdk in reversed(sdks): # Newest SDK is always at the end of the list. - if not hasattr(sdk, 'bitness') or sdk.bitness == 64: # If no 'bitness' field, it means the SDK is universal. - if 'nightly' in sdk.version: - return sdk - return None - - -def find_latest_nightly_sdk(): - if is_os_64bit(): - return find_latest_nightly_64bit_sdk() - else: - return find_latest_nightly_32bit_sdk() - - -def find_latest_waterfall_sdk(which): - waterfall_lkgr = load_waterfall_lkgr() - if not waterfall_lkgr: - print('Failed to find an upstream lkgr') - sys.exit(1) - return 'sdk-waterfall-%s-%s-64bit' % (which, waterfall_lkgr[0]) - - -def find_latest_releases_sdk(which): - releases_info = load_releases_info() - latest = releases_info['latest'] - ident = releases_info['releases'][latest] - return 'sdk-releases-%s-%s-64bit' % (which, ident) - - -# Given a git hash in emscripten-releases, find the emscripten -# version for it. There may not be one if this is not the hash of -# a release, in which case we just use the hash. -def get_emscripten_release_version(emscripten_releases_hash): - releases_info = load_releases_info() - for key, value in dict(releases_info['releases']).items(): - if value == emscripten_releases_hash: - return key - return emscripten_releases_hash - - -# Finds the best-matching python tool for use. -def find_used_python(): - for t in reversed(tools): # Find newest tool first - those are always at the end of the list. - if t.id == 'python' and t.is_installed() and t.is_active() and t.is_env_active(): - return t - for t in reversed(tools): - if t.id == 'python' and t.is_installed() and t.is_active(): - return t - for t in reversed(tools): - if t.id == 'python' and t.is_installed(): - return t - return None - - -def version_key(ver): - return list(map(int, re.split('[._-]', ver))) - - -# A sort function that is compatible with both Python 2 and Python 3 using a custom comparison function. -def python_2_3_sorted(arr, cmp): - if sys.version_info >= (3,): - return sorted(arr, key=functools.cmp_to_key(cmp)) - else: - return sorted(arr, cmp=cmp) - - -def fetch_emscripten_tags(): - git = GIT(must_succeed=False) - - if git: - print('Fetching all tags from Emscripten Github repository...') - tags = run_get_output([git, 'ls-remote', '--tags', emscripten_git_repo]) - if tags[0] != 0: return False # failed to get tags? - tags = tags[1] - all_tags = [] - for t in tags.split('\n'): - try: - t = t[t.index('refs/tags/') + len('refs/tags/'):].strip() - if version_key(t) >= version_key('1.28.2'): - all_tags += [t] - except: - pass - all_tags = sorted(all_tags, key=version_key) - open(sdk_path('emscripten-tags.txt'), 'w').write('\n'.join(all_tags)) - if len(all_tags) > 0: - print('Done. ' + str(len(all_tags)) + ' tagged releases available, latest is ' + all_tags[-1] + '.') - else: - print('Done. No tagged releases available.') - - print('Fetching all tags from Binaryen Github repository...') - tags = run_get_output([git, 'ls-remote', '--tags', binaryen_git_repo]) - if tags[0] != 0: return False # failed to get tags? - tags = tags[1] - binaryen_tags = [] - for t in tags.split('\n'): - try: - t = t[t.index('refs/tags/') + len('refs/tags/'):].strip() - if '.' in t: - binaryen_tags += [t] - except: - pass - binaryen_tags = sorted(binaryen_tags, key=version_key) - open(sdk_path('binaryen-tags.txt'), 'w').write('\n'.join(binaryen_tags)) - if len(binaryen_tags) > 0: - print('Done. ' + str(len(binaryen_tags)) + ' tagged Binaryen releases available, latest is ' + binaryen_tags[-1] + '.') - else: - print('Done. No tagged Binaryen releases available.') - -# Emscripten Nightlies support has been removed, clear the list of known Nightlies locally. -# print('Fetching all precompiled Nightly versions..') -# download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/nightly/' + os_name() + '_32bit/index.txt', 'llvm-nightlies-32bit.txt', download_even_if_exists=True) -# download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/nightly/' + os_name() + '_64bit/index.txt', 'llvm-nightlies-64bit.txt', download_even_if_exists=True) -# download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/emscripten/nightly/' + os_name() + '/index.txt', 'emscripten-nightlies.txt', download_even_if_exists=True) - for f in ['llvm-nightlies-32bit.txt', 'llvm-nightlies-64bit.txt', 'emscripten-nightlies.txt']: - if os.path.isfile(f): os.remove(f) - - print('Fetching all precompiled tagged releases..') - download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/tag/' + os_name() + '_32bit/index.txt', 'llvm-tags-32bit.txt', download_even_if_exists=True) - download_file('https://s3.amazonaws.com/mozilla-games/emscripten/packages/llvm/tag/' + os_name() + '_64bit/index.txt', 'llvm-tags-64bit.txt', download_even_if_exists=True) - download_waterfall_lkgr() - - if not git: - print('Update complete, however skipped fetching the Emscripten tags, since git was not found.') - if WINDOWS: - print("If you want to compile one of the tagged releases from source, please install git by typing 'emsdk install git-1.9.4', or alternatively by installing it manually from http://git-scm.com/downloads . If you install git manually, remember to add it to PATH.") - elif OSX: - print("If you want to compile one of the tagged releases from source, please install git from http://git-scm.com/ , or by installing XCode and then the XCode Command Line Tools (see http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools ).") - elif LINUX: - print("If you want to compile one of the tagged releases from source, please install git using your package manager, see http://git-scm.com/book/en/Getting-Started-Installing-Git .") - else: - print("If you want to compile one of the tagged releases from source, please install git.") - print("If you are not looking to build Emscripten from source, you can safely ignore this message.") - return - - -def is_emsdk_sourced_from_github(): - return os.path.exists(os.path.join(emsdk_path(), '.git')) - - -def update_emsdk(): - if is_emsdk_sourced_from_github(): - print('You seem to have bootstrapped Emscripten SDK by cloning from GitHub. In this case, use "git pull" instead of "emsdk update" to update emsdk. (Not doing that automatically in case you have local changes)', file=sys.stderr) - print('Alternatively, use "emsdk update-tags" to refresh the latest list of tags from the different Git repositories.', file=sys.stderr) - sys.exit(1) - if WINDOWS: - download_and_unzip(urljoin(emsdk_packages_url, 'emsdk_windows_update.zip'), emsdk_path(), download_even_if_exists=True) - rmfile('zips/emsdk_windows_update.zip') - elif OSX or LINUX: - download_and_unzip(urljoin(emsdk_packages_url, 'emsdk_unix_update.tar.gz'), emsdk_path(), download_even_if_exists=True) - rmfile('zips/emsdk_unix_update.tar.gz') - else: - print('Unsupported OS, cannot update!') - fetch_emscripten_tags() - - -# Lists all tagged versions directly in the Git repositories. These we can pull and compile from source. -def load_emscripten_tags(): - try: - return open(sdk_path('emscripten-tags.txt'), 'r').read().split('\n') - except: - return [] - - -def load_binaryen_tags(): - try: - return open(sdk_path('binaryen-tags.txt'), 'r').read().split('\n') - except: - return [] - - -def remove_prefix(s, prefix): - if s.startswith(prefix): return s[len(prefix):] - else: return s - - -def remove_suffix(s, suffix): - if s.endswith(suffix): return s[:len(s) - len(suffix)] - else: return s - - -# filename should be one of: 'llvm-nightlies-32bit.txt', 'llvm-nightlies-64bit.txt', 'llvm-precompiled-tags-32bit.txt', 'llvm-precompiled-tags-64bit.txt', 'emscripten-nightlies.txt' -def load_file_index_list(filename): - try: - items = open(sdk_path(filename), 'r').read().split('\n') - items = map(lambda x: remove_suffix(remove_suffix(remove_prefix(remove_prefix(x, 'emscripten-llvm-e'), 'emscripten-nightly-'), '.tar.gz'), '.zip').strip(), items) - items = filter(lambda x: 'latest' not in x and len(x) > 0, items) - - # Sort versions from oldest to newest (the default sort would be lexicographic, i.e. '1.37.1 < 1.37.10 < 1.37.2') - items = sorted(items, key=version_key)[::-1] - - return items - except: - return [] - - -def load_llvm_32bit_nightlies(): - return load_file_index_list('llvm-nightlies-32bit.txt') - - -def load_llvm_64bit_nightlies(): - return load_file_index_list('llvm-nightlies-64bit.txt') - - -def load_emscripten_nightlies(): - return load_file_index_list('emscripten-nightlies.txt') - - -def load_llvm_precompiled_tags_32bit(): - return load_file_index_list('llvm-tags-32bit.txt') - - -def load_llvm_precompiled_tags_64bit(): - return load_file_index_list('llvm-tags-64bit.txt') - - -def download_waterfall_lkgr(): - lkgr_url = 'https://storage.googleapis.com/wasm-llvm/builds/%s/lkgr.json' % os_name() - download_file(lkgr_url, 'upstream', download_even_if_exists=True) - - -def load_waterfall_lkgr(): - try: - text = open(sdk_path(os.path.join('upstream', 'lkgr.json')), 'r').read() - data = json.loads(text) - lkgr = data['build'] - return [lkgr] - except Exception as e: - print('Error parsing lkgr.json!') - print(str(e)) - return [] - - -# Load the json info for emscripten-releases. -def load_releases_info(): - try: - text = open(sdk_path('emscripten-releases-tags.txt'), 'r').read() - return json.loads(text) - except Exception as e: - print('Error parsing emscripten-releases-tags.txt!') - print(str(e)) - sys.exit(1) - - -# Get a list of tags for emscripten-releases. -def load_releases_tags(): - info = load_releases_info() - return list(info['releases'].values()) - - -def is_string(s): - if sys.version_info[0] >= 3: - return isinstance(s, str) - return isinstance(s, basestring) - - -def load_sdk_manifest(): - global tools, sdks - try: - manifest = json.loads(open(sdk_path("emsdk_manifest.json"), "r").read()) - except Exception as e: - print('Error parsing emsdk_manifest.json!') - print(str(e)) - return - - emscripten_tags = load_emscripten_tags() - llvm_precompiled_tags_32bit = list(reversed(load_llvm_precompiled_tags_32bit())) - llvm_precompiled_tags_64bit = list(reversed(load_llvm_precompiled_tags_64bit())) - llvm_precompiled_tags = llvm_precompiled_tags_32bit + llvm_precompiled_tags_64bit - binaryen_tags = load_binaryen_tags() - llvm_32bit_nightlies = list(reversed(load_llvm_32bit_nightlies())) - llvm_64bit_nightlies = list(reversed(load_llvm_64bit_nightlies())) - emscripten_nightlies = list(reversed(load_emscripten_nightlies())) - waterfall_lkgr = load_waterfall_lkgr() - releases_tags = load_releases_tags() - - def dependencies_exist(sdk): - for tool_name in sdk.uses: - tool = find_tool(tool_name) - if not tool: return False - return True - - def cmp_version(ver, cmp_operand, reference): - if cmp_operand == '<=': return version_key(ver) <= version_key(reference) - if cmp_operand == '<': return version_key(ver) < version_key(reference) - if cmp_operand == '>=': return version_key(ver) >= version_key(reference) - if cmp_operand == '>': return version_key(ver) > version_key(reference) - if cmp_operand == '==': return version_key(ver) == version_key(reference) - if cmp_operand == '!=': return version_key(ver) != version_key(reference) - raise Exception('Invalid cmp_operand "' + cmp_operand + '"!') - - def passes_filters(param, ver, filters): - for v in filters: - if v[0] == param and not cmp_version(ver, v[1], v[2]): - return False - return True - - # A 'category parameter' is a %foo%-encoded identifier that specifies - # a class of tools instead of just one tool, e.g. %tag% or %nightly..% - def expand_category_param(param, category_list, t, is_sdk): - global tools, sdks - for i in range(len(category_list)): - ver = category_list[i] - if len(ver.strip()) == 0: continue - t2 = copy.copy(t) - found_param = False - for p, v in vars(t2).items(): - if is_string(v) and param in v: - t2.__dict__[p] = v.replace(param, ver) - found_param = True - if not found_param: continue - t2.is_old = i < len(category_list) - 2 - if hasattr(t2, 'uses'): - t2.uses = list(map((lambda x: x.replace(param, ver)), t2.uses)) - - # Filter out expanded tools by version requirements, such as ["tag", "<=", "1.37.22"] - if hasattr(t2, 'version_filter'): - passes = passes_filters(param, ver, t2.version_filter) - if not passes: continue - - if is_sdk: - if dependencies_exist(t2): - if not find_sdk(t2.name): - add_sdk(t2) - elif VERBOSE: print('SDK ' + str(t2) + ' already existed in manifest, not adding twice') - else: - if not find_tool(t2.name): - add_tool(t2) - elif VERBOSE: print('Tool ' + str(t2) + ' already existed in manifest, not adding twice') - - for tool in manifest['tools']: - t = Tool(tool) - if t.compatible_with_this_os(): - if not hasattr(t, 'is_old'): - t.is_old = False - - # Expand the metapackages that refer to tags or nightlies. - if '%tag%' in t.version: expand_category_param('%tag%', emscripten_tags, t, is_sdk=False) - elif '%precompiled_tag%' in t.version: expand_category_param('%precompiled_tag%', llvm_precompiled_tags, t, is_sdk=False) - elif '%precompiled_tag32%' in t.version: expand_category_param('%precompiled_tag32%', llvm_precompiled_tags_32bit, t, is_sdk=False) - elif '%precompiled_tag64%' in t.version: expand_category_param('%precompiled_tag64%', llvm_precompiled_tags_64bit, t, is_sdk=False) - elif '%binaryen_tag%' in t.version: expand_category_param('%binaryen_tag%', binaryen_tags, t, is_sdk=False) - elif '%nightly-llvm-64bit%' in t.version: expand_category_param('%nightly-llvm-64bit%', llvm_64bit_nightlies, t, is_sdk=False) - elif '%nightly-llvm-32bit%' in t.version: expand_category_param('%nightly-llvm-32bit%', llvm_32bit_nightlies, t, is_sdk=False) - elif '%nightly-emscripten%' in t.version: expand_category_param('%nightly-emscripten%', emscripten_nightlies, t, is_sdk=False) - elif '%waterfall-lkgr%' in t.version: expand_category_param('%waterfall-lkgr%', waterfall_lkgr, t, is_sdk=False) - elif '%releases-tag%' in t.version: expand_category_param('%releases-tag%', releases_tags, t, is_sdk=False) - else: - add_tool(t) - - for sdk_str in manifest['sdks']: - sdk_str['id'] = 'sdk' - sdk = Tool(sdk_str) - if sdk.compatible_with_this_os(): - if not hasattr(sdk, 'is_old'): - sdk.is_old = False - - if '%tag%' in sdk.version: expand_category_param('%tag%', emscripten_tags, sdk, is_sdk=True) - elif '%precompiled_tag%' in sdk.version: expand_category_param('%precompiled_tag%', llvm_precompiled_tags, sdk, is_sdk=True) - elif '%precompiled_tag32%' in sdk.version: expand_category_param('%precompiled_tag32%', llvm_precompiled_tags_32bit, sdk, is_sdk=True) - elif '%precompiled_tag64%' in sdk.version: expand_category_param('%precompiled_tag64%', llvm_precompiled_tags_64bit, sdk, is_sdk=True) - elif '%nightly-llvm-64bit%' in sdk.version: expand_category_param('%nightly-llvm-64bit%', llvm_64bit_nightlies, sdk, is_sdk=True) - elif '%nightly-llvm-32bit%' in sdk.version: expand_category_param('%nightly-llvm-32bit%', llvm_32bit_nightlies, sdk, is_sdk=True) - elif '%nightly-emscripten%' in sdk.version: expand_category_param('%nightly-emscripten%', emscripten_nightlies, sdk, is_sdk=True) - elif '%waterfall-lkgr%' in sdk.version: expand_category_param('%waterfall-lkgr%', waterfall_lkgr, sdk, is_sdk=True) - elif '%releases-tag%' in sdk.version: expand_category_param('%releases-tag%', releases_tags, sdk, is_sdk=True) - else: - add_sdk(sdk) - - -# Tests if the two given tools can be active at the same time. -# Currently only a simple check for name for same tool with different versions, -# possibly adds more logic in the future. -def can_simultaneously_activate(tool1, tool2): - return tool1.id != tool2.id - - -def remove_nonexisting_tools(tool_list, log_errors=True): - i = 0 - while i < len(tool_list): - tool = tool_list[i] - if not tool.is_installed(): - if log_errors: - print("Warning: The SDK/tool '" + str(tool) + "' cannot be activated since it is not installed! Skipping this tool...") - tool_list.pop(i) - continue - i += 1 - return tool_list - - -# Expands dependencies for each tool, and removes ones that don't exist. -def process_tool_list(tools_to_activate, log_errors=True): - i = 0 - # Gather dependencies for each tool - while i < len(tools_to_activate): - tool = tools_to_activate[i] - deps = tool.recursive_dependencies() - tools_to_activate = tools_to_activate[:i] + deps + tools_to_activate[i:] - i += len(deps) + 1 - - tools_to_activate = remove_nonexisting_tools(tools_to_activate, log_errors=log_errors) - - # Remove conflicting tools - i = 0 - while i < len(tools_to_activate): - j = 0 - while j < i: - secondary_tool = tools_to_activate[j] - primary_tool = tools_to_activate[i] - if not can_simultaneously_activate(primary_tool, secondary_tool): - tools_to_activate.pop(j) - j -= 1 - i -= 1 - j += 1 - i += 1 - return tools_to_activate - - -# Reconfigure .emscripten to choose the currently activated toolset, set PATH and other environment variables. -# Returns the full list of deduced tools that are now active. -def set_active_tools(tools_to_activate, permanently_activate): - tools_to_activate = process_tool_list(tools_to_activate, log_errors=True) - - generate_dot_emscripten(tools_to_activate) - - # Construct a .bat script that will be invoked to set env. vars and PATH - if WINDOWS: - env_string = construct_env(tools_to_activate, False) - open(EMSDK_SET_ENV, 'w').write(env_string) - - # Apply environment variables to global all users section. - if WINDOWS and permanently_activate: - # Individual env. vars - for tool in tools_to_activate: - tool.win_activate_env_vars(permanently_activate=True) - - # PATH variable - newpath, added_items = adjusted_path(tools_to_activate, system_path_only=True) - if newpath != os.environ['PATH']: # Are there any actual changes? - win_set_environment_variable('PATH', newpath, system=True) - - if len(tools_to_activate) > 0: - tools = filter(lambda x: not x.is_sdk, tools_to_activate) - print('Set the following tools as active:\n ' + '\n '.join(map(lambda x: str(x), tools))) - print('') - return tools_to_activate - - -def currently_active_sdk(): - for sdk in reversed(sdks): - if sdk.is_active(): - return sdk - return None - - -def currently_active_tools(): - active_tools = [] - for tool in tools: - if tool.is_active(): - active_tools += [tool] - return active_tools - - -# http://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-python-whilst-preserving-order -def unique_items(seq): - seen = set() - seen_add = seen.add - return [x for x in seq if x not in seen and not seen_add(x)] - - -# Tests if a path is contained in the given list, but with separators normalized. -def normalized_contains(lst, elem): - elem = to_unix_path(elem) - for e in lst: - if elem == to_unix_path(e): - return True - return False - - -def to_msys_path(p): - p = to_unix_path(p) - new_path = re.sub(r'([a-zA-Z]):/(.*)', r'/\1/\2', p) - if len(new_path) > 3 and new_path[0] == '/' and new_path[2] == '/': - new_path = new_path[0] + new_path[1].lower() + new_path[2:] - return new_path - - -# Looks at the current PATH and adds and removes entries so that the PATH reflects -# the set of given active tools. -def adjusted_path(tools_to_activate, log_additions=False, system_path_only=False): - # These directories should be added to PATH - path_add = get_required_path(tools_to_activate) - # These already exist. - if WINDOWS and not MSYS: - existing_path = win_get_environment_variable('PATH', system=True) - if not system_path_only: - current_user_path = win_get_environment_variable('PATH', system=False) - if current_user_path: existing_path += ENVPATH_SEPARATOR + current_user_path - existing_path = existing_path.split(ENVPATH_SEPARATOR) - - # Fix up after potential changes made by bug https://github.com/kripken/emscripten/issues/4121 - system_root = os.environ['SystemRoot'].lower() - for i in range(len(existing_path)): - p = existing_path[i] - if p.lower() == system_root: p = '%SystemRoot%' - elif (system_root + '\\system32') in p.lower(): p = '%SystemRoot%\\system32' - elif (system_root + '\\system32\\wbem') in p.lower(): p = '%SystemRoot%\\System32\\Wbem' - elif (system_root + '\\system32\\windowspowershell\v1.0') in p.lower(): p = '%SystemRoot%\\System32\\WindowsPowerShell\v1.0\\' - existing_path[i] = p - else: - existing_path = os.environ['PATH'].split(ENVPATH_SEPARATOR) - emsdk_root_path = to_unix_path(emsdk_path()) - - existing_emsdk_tools = [item for item in existing_path if to_unix_path(item).startswith(emsdk_root_path)] - new_emsdk_tools = [item for item in path_add if not normalized_contains(existing_emsdk_tools, item)] - - # Existing non-emsdk tools - existing_path = [item for item in existing_path if not to_unix_path(item).startswith(emsdk_root_path)] - new_path = [item for item in path_add if not normalized_contains(existing_path, item)] - whole_path = unique_items(new_path + existing_path) - if MSYS: - # XXX Hack: If running native Windows Python in MSYS prompt where PATH entries look like "/c/Windows/System32", os.environ['PATH'] - # in Python will transform to show them as "C:\\Windows\\System32", so need to reconvert path delimiter back to forward slashes. - whole_path = list(map(to_msys_path, whole_path)) - new_emsdk_tools = list(map(to_msys_path, new_emsdk_tools)) - - return ((':' if MSYS else ENVPATH_SEPARATOR).join(whole_path), new_emsdk_tools) - - -def construct_env(tools_to_activate, permanent): - global emscripten_config_directory - env_string = '' - newpath, added_path = adjusted_path(tools_to_activate) - -# Dont permanently add to PATH, since this will break the whole system if there are more than 1024 chars in PATH. -# (SETX truncates to set only 1024 chars) -# if permanent: -# print('SETX PATH "' + newpath + '"') -# else: - - if os.environ['PATH'] != newpath: # Don't bother setting the path if there are no changes. - if POWERSHELL: env_string += '$env:PATH="' + newpath + '"\n' - elif WINDOWS and not MSYS: env_string += 'SET PATH=' + newpath + '\n' - else: env_string += 'export PATH="' + newpath + '"\n' - if len(added_path) > 0: - print('Adding directories to PATH:') - for item in added_path: - print('PATH += ' + item) - print('') - - env_vars_to_add = [] - - # A core variable EMSDK points to the root of Emscripten SDK directory. - env_vars_to_add += [('EMSDK', to_unix_path(emsdk_path()))] - - em_config_path = os.path.normpath(dot_emscripten_path()) - if 'EM_CONFIG' not in os.environ or to_unix_path(os.environ['EM_CONFIG']) != to_unix_path(em_config_path): - env_vars_to_add += [('EM_CONFIG', em_config_path)] - if emscripten_config_directory == emsdk_path(): - em_cache_dir = sdk_path('.emscripten_cache') - if 'EM_CACHE' not in os.environ or to_unix_path(os.environ['EM_CACHE']) != to_unix_path(em_cache_dir): - env_vars_to_add += [('EM_CACHE', em_cache_dir)] - mkdir_p(em_cache_dir) - - for tool in tools_to_activate: - envs = tool.activated_environment() - for env in envs: - (key, value) = parse_key_value(env) - value = to_native_path(tool.expand_vars(value)) - if key not in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value): # Don't set env. vars which are already set to the correct value. - env_vars_to_add += [(key, value)] - - if len(env_vars_to_add) > 0: - print('Setting environment variables:') - for key, value in env_vars_to_add: - if POWERSHELL: - env_string += '$env:' + key + '="' + value + '"\n' - elif WINDOWS and not MSYS: - if permanent: - env_string += 'SETX ' + key + ' "' + value + '"\n' - else: - env_string += 'SET ' + key + '=' + value + '\n' - else: - env_string += 'export ' + key + '="' + value + '"\n' - print(key + ' = ' + value) - print('') - return env_string - - -def silentremove(filename): - try: - os.remove(filename) - except OSError as e: - if e.errno != errno.ENOENT: raise - - -def main(): - global emscripten_config_directory, BUILD_FOR_TESTING, ENABLE_LLVM_ASSERTIONS, TTY_OUTPUT - - if len(sys.argv) <= 1 or sys.argv[1] == 'help' or sys.argv[1] == '--help': - if len(sys.argv) <= 1: - print(' emsdk: No command given. Please call one of the following:') - else: - print(' emsdk: Available commands:') - - print(''' - emsdk list [--old] [--uses] - Lists all available SDKs and tools and their - current installation status. With the --old - parameter, also historical versions are - shown. If --uses is passed, displays the - composition of different SDK packages and - dependencies. - - emsdk update - Updates emsdk to the newest version, and also - runs 'update-tags' (below). If you have - bootstrapped emsdk via cloning directly from - GitHub, call "git pull" instead to update emsdk. - - emsdk update-tags - Fetches the most up to date list of available - Emscripten tagged and nightly releases. - - emsdk install [options] ... - - Downloads and installs given tools or SDKs. - Options can contain: - - -j: Specifies the number of cores to use when - building the tool. Default: use one less - than the # of detected cores. - - --build=: Controls what kind of build of LLVM to - perform. Pass either 'Debug', 'Release', - 'MinSizeRel' or 'RelWithDebInfo'. Default: - 'Release' for LLVM master branch, and - 'RelWithDebInfo' for LLVM incoming branch. - - --shallow: When installing tools from one of the git - development branches 'master' or 'incoming', - this parameter can be passed to perform a - shallow git clone instead of a full one. - This reduces the amount of network transfer - that is needed. This option should only be - used when you are interested in downloading - one of the development branches, but are not - looking to develop Emscripten yourself. - Default: disabled, i.e. do a full clone. - - --build-tests: If enabled, LLVM is built with internal tests - included. Pass this to enable running test - other.test_llvm_lit in the Emscripten test - suite. Default: disabled. - --enable-assertions: If specified, LLVM is built with assert() - checks enabled. Useful for development - purposes. Default: Enabled for 'incoming' - branch, disabled for 'master' branch. - --disable-assertions: Forces assertions off during the build. - - --vs2013/--vs2015/--vs2017: If building from source, overrides to build - using the specified compiler. When installing - precompiled packages, this has no effect. - Note: The same compiler specifier must be - passed to the emsdk activate command to - activate the desired version. - - --enable-wasm: Enable WebAssembly support in the - installed components. - - To pass custom CMake directives when configuring - LLVM build, specify the environment variable - LLVM_CMAKE_ARGS="param1=value1,param2=value2" - in the environment where the build is invoked. - See README.md for details. - - emsdk uninstall - Removes the given tool or SDK from disk.''') - - if WINDOWS: - print(''' - emsdk activate [--global] [--embedded] [--build=type] [--vs2013/--vs2015/--vs2017] - - - Activates the given tool or SDK in the - environment of the current shell. If the - --global option is passed, the registration - is done globally to all users in the system - environment. If the --embedded option is - passed, all Emcripten configuration files as - well as the temp, cache and ports directories - are located inside the Emscripten SDK - directory rather than the user home - directory. If a custom compiler version was - used to override the compiler to use, pass - the same --vs2013/--vs2015/--vs2017 parameter - here to choose which version to activate. - - emcmdprompt.bat - Spawns a new command prompt window with the - Emscripten environment active.''') - else: - print(''' emsdk activate [--embedded] [--build=type] - - - Activates the given tool or SDK in the - environment of the current shell. If the - --embedded option is passed, all Emcripten - configuration files as well as the temp, cache - and ports directories are located inside the - Emscripten SDK directory rather than the user - home directory.''') - - print(''' - Both commands 'install' and 'activate' accept an optional parameter - '--build=type', which can be used to override what kind of installation - or activation to perform. Possible values for type are Debug, Release, - MinSizeRel or RelWithDebInfo. Note: When overriding a custom build type, - be sure to match the same --build= option to both 'install' and - 'activate' commands and the invocation of 'emsdk_env', or otherwise - these commands will default to operating on the default build types, - which are Release for the 'master' SDK, and RelWithDebInfo for the - 'incoming' SDK.''') - return 1 - - # Extracts a boolean command line argument from sys.argv and returns True if it was present - def extract_bool_arg(name): - old_argv = sys.argv - sys.argv = list(filter(lambda a: a != name, sys.argv)) - return len(sys.argv) != len(old_argv) - - arg_old = extract_bool_arg('--old') - arg_uses = extract_bool_arg('--uses') - arg_global = extract_bool_arg('--global') - arg_embedded = extract_bool_arg('--embedded') - arg_notty = extract_bool_arg('--notty') - if arg_notty: - TTY_OUTPUT = False - - cmd = sys.argv[1] - - # On first run when tag list is not present, populate it to bootstrap. - if (cmd == 'install' or cmd == 'list') and not os.path.isfile(sdk_path('llvm-tags-64bit.txt')): - fetch_emscripten_tags() - - load_dot_emscripten() - load_sdk_manifest() - - # Process global args - for i in range(2, len(sys.argv)): - if sys.argv[i].startswith('--build='): - build_type = re.match(r'^--build=(.+)$', sys.argv[i]) - if build_type: - global CMAKE_BUILD_TYPE_OVERRIDE - build_type = build_type.group(1) - build_types = ['Debug', 'MinSizeRel', 'RelWithDebInfo', 'Release'] - try: - build_type_index = [x.lower() for x in build_types].index(build_type.lower()) - CMAKE_BUILD_TYPE_OVERRIDE = build_types[build_type_index] - sys.argv[i] = '' - except: - print('Unknown CMake build type "' + build_type + '" specified! Please specify one of ' + str(build_types), file=sys.stderr) - return 1 - else: - print("Invalid command line parameter " + sys.argv[i] + ' specified!', file=sys.stderr) - return 1 - sys.argv = [x for x in sys.argv if not len(x) == 0] - - # Replace meta-packages with the real package names. - if (cmd == 'update' or cmd == 'install' or cmd == 'activate'): - for i in range(2, len(sys.argv)): - if sys.argv[i] == 'latest' or sys.argv[i] == 'sdk-latest': - sys.argv[i] = str(find_latest_sdk()) - elif sys.argv[i] == 'latest-32bit' or sys.argv[i] == 'sdk-latest-32bit': - sys.argv[i] = str(find_latest_32bit_sdk()) - elif sys.argv[i] == 'latest-64bit' or sys.argv[i] == 'sdk-latest-64bit': - sys.argv[i] = str(find_latest_64bit_sdk()) - elif sys.argv[i] == 'sdk-nightly-latest': - sys.argv[i] = str(find_latest_nightly_sdk()) - elif sys.argv[i] == 'sdk-nightly-latest-32bit': - sys.argv[i] = str(find_latest_nightly_32bit_sdk()) - elif sys.argv[i] == 'sdk-nightly-latest-64bit': - sys.argv[i] = str(find_latest_nightly_64bit_sdk()) - elif sys.argv[i] == 'latest-upstream' or sys.argv[i] == 'latest-clang-upstream': - sys.argv[i] = str(find_latest_waterfall_sdk('upstream')) - elif sys.argv[i] == 'latest-fastcomp': - sys.argv[i] = str(find_latest_waterfall_sdk('fastcomp')) - elif sys.argv[i] == 'latest-releases-upstream': - sys.argv[i] = str(find_latest_releases_sdk('upstream')) - elif sys.argv[i] == 'latest-releases-fastcomp': - sys.argv[i] = str(find_latest_releases_sdk('fastcomp')) - - if cmd == 'list': - print('') - has_partially_active_tools = [False] # Use array to work around the lack of being able to mutate from enclosing function. - if len(tools) > 0: - def find_tools(needs_compilation): - t = [] - for tool in tools: - if tool.is_old and not arg_old: - continue - if tool.needs_compilation() != needs_compilation: - continue - t += [tool] - return t - - def print_tools(t): - for tool in t: - if tool.is_old and not arg_old: - continue - if tool.can_be_installed(): - installed = '\tINSTALLED' if tool.is_installed() else '' - else: - installed = '\tNot available: ' + tool.can_be_installed() - tool_is_active = tool.is_active() - tool_is_env_active = tool_is_active and tool.is_env_active() - if tool_is_env_active: active = ' * ' - elif tool_is_active: - active = '(*)' - has_partially_active_tools[0] = has_partially_active_tools[0] or True - else: active = ' ' - print(' ' + active + ' {0: <25}'.format(str(tool)) + installed) - print('') - - print('The following precompiled tool packages are available for download:') - print_tools(find_tools(False)) - print('The following tools can be compiled from source:') - print_tools(find_tools(True)) - else: - if is_emsdk_sourced_from_github(): - print("There are no tools available. Run 'git pull' followed by 'emsdk update-tags' to fetch the latest set of tools.") - else: - print("There are no tools available. Run 'emsdk update' to fetch the latest set of tools.") - print('') - - if len(sdks) > 0: - def find_sdks(needs_compilation): - s = [] - for sdk in sdks: - if sdk.is_old and not arg_old: - continue - if sdk.needs_compilation() == needs_compilation: - s += [sdk] - return s - - def print_sdks(s): - for sdk in s: - installed = '\tINSTALLED' if sdk.is_installed() else '' - active = '*' if sdk.is_active() else ' ' - print(' ' + active + ' {0: <25}'.format(str(sdk)) + installed) - if arg_uses: - for dep in sdk.uses: - print(' - {0: <25}'.format(dep)) - print('') - if is_emsdk_sourced_from_github(): - print('The following precompiled SDKs are available for download: (Run "git pull" followed by "./emsdk update-tags" to pull in the latest list)') - else: - print('The following precompiled SDKs are available for download: (Run "./emsdk update" to pull in the latest list)') - print_sdks(find_sdks(False)) - print('The following SDKs can be compiled from source:') - print_sdks(find_sdks(True)) - - print('Items marked with * are activated for the current user.') - if has_partially_active_tools[0]: - env_cmd = 'emsdk_env.bat' if WINDOWS else 'source ./emsdk_env.sh' - print('Items marked with (*) are selected for use, but your current shell environment is not configured to use them. Type "' + env_cmd + '" to set up your current shell to use them' + (', or call "emsdk activate --global " to permanently activate them.' if WINDOWS else '.')) - if not arg_old: - print('') - print("To access the historical archived versions, type 'emsdk list --old'") - - return 0 - elif cmd == 'construct_env': - silentremove(EMSDK_SET_ENV) # Clean up old temp file up front, in case of failure later before we get to write out the new one. - tools_to_activate = currently_active_tools() - tools_to_activate = process_tool_list(tools_to_activate, log_errors=True) - env_string = construct_env(tools_to_activate, len(sys.argv) >= 3 and 'perm' in sys.argv[2]) - open(EMSDK_SET_ENV, 'w').write(env_string) - if LINUX or OSX: - os.chmod(EMSDK_SET_ENV, 0o755) - return 0 - elif cmd == 'update': - update_emsdk() - silentremove(sdk_path(EMSDK_SET_ENV)) # Clean up litter after old emsdk update which may have left this temp file around. - return 0 - elif cmd == 'update-tags': - fetch_emscripten_tags() - return 0 - elif cmd == 'activate': - if arg_global: - print('Registering active Emscripten environment globally for all users.') - print('') - if arg_embedded: - # Activating the emsdk tools locally relative to Emscripten SDK directory. - emscripten_config_directory = emsdk_path() - print('Writing .emscripten configuration file to Emscripten SDK directory ' + emscripten_config_directory) - else: - print('Writing .emscripten configuration file to user home directory ' + emscripten_config_directory) - # Remove .emscripten from emsdk dir, since its presence is used to detect whether emsdk is activate in embedded mode or not. - try: - os.remove(os.path.join(emsdk_path(), ".emscripten")) - except: - pass - - sys.argv = [x for x in sys.argv if not x.startswith('--')] - - tools_to_activate = currently_active_tools() - for i in range(2, len(sys.argv)): - tool = find_tool(sys.argv[i]) - if tool is None: - tool = find_sdk(sys.argv[i]) - if tool is None: - print("Error: No tool or SDK found by name '" + sys.argv[i] + "'.") - return 1 - tools_to_activate += [tool] - if len(tools_to_activate) == 0: - print('No tools/SDKs specified to activate! Usage:\n emsdk activate tool/sdk1 [tool/sdk2] [...]') - return 1 - tools_to_activate = set_active_tools(tools_to_activate, permanently_activate=arg_global) - if len(tools_to_activate) == 0: - print('No tools/SDKs found to activate! Usage:\n emsdk activate tool/sdk1 [tool/sdk2] [...]') - return 1 - if WINDOWS and not arg_global: - print('The changes made to environment variables only apply to the currently running shell instance. Use the \'emsdk_env.bat\' to re-enter this environment later, or if you\'d like to permanently register this environment globally to all users in Windows Registry, rerun this command with the option --global.') - return 0 - elif cmd == 'install': - # Process args - for i in range(2, len(sys.argv)): - if sys.argv[i].startswith('-j'): - multicore = re.match(r'^-j(\d+)$', sys.argv[i]) - if multicore: - global CPU_CORES - CPU_CORES = int(multicore.group(1)) - sys.argv[i] = '' - else: - print("Invalid command line parameter " + sys.argv[i] + ' specified!', file=sys.stderr) - return 1 - elif sys.argv[i] == '--shallow': - global GIT_CLONE_SHALLOW - GIT_CLONE_SHALLOW = True - sys.argv[i] = '' - elif sys.argv[i] == '--build-tests': - BUILD_FOR_TESTING = True - sys.argv[i] = '' - elif sys.argv[i] == '--enable-assertions': - ENABLE_LLVM_ASSERTIONS = 'ON' - sys.argv[i] = '' - elif sys.argv[i] == '--disable-assertions': - ENABLE_LLVM_ASSERTIONS = 'OFF' - sys.argv[i] = '' - elif sys.argv[i] == '--enable-wasm': - global ENABLE_WASM - ENABLE_WASM = True - sys.argv[i] = '' - sys.argv = [x for x in sys.argv if not len(x) == 0] - if len(sys.argv) <= 2: - print("Missing parameter. Type 'emsdk install ' to install a tool or an SDK. Type 'emsdk list' to obtain a list of available tools. Type 'emsdk install latest' to automatically install the newest version of the SDK.") - return 1 - for t in sys.argv[2:]: - tool = find_tool(t) - if tool is None: - tool = find_sdk(t) - if tool is None: - print("Error: No tool or SDK found by name '" + t + "'.") - return 1 - success = tool.install() - if not success: - return 1 - elif cmd == 'uninstall': - if len(sys.argv) <= 2: - print("Syntax error. Call 'emsdk uninstall '. Call 'emsdk list' to obtain a list of available tools.") - return 1 - tool = find_tool(sys.argv[2]) - if tool is None: - print("Error: Tool by name '" + sys.argv[2] + "' was not found.") - return 1 - tool.uninstall() - else: - print("Unknown command '" + cmd + "' given! Type 'emsdk help' to get a list of commands.") - return 1 - - -if __name__ == '__main__': - sys.exit(main()) +#!/bin/sh +# Copyright 2019 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +# Wrapper script that runs emsdk.py + +# First look for python bundled in Emsdk +if [ -z "$EMSDK_PYTHON" ]; then + PYTHON3=$(dirname $0)/python/3.9.2-1_64bit/bin/python3 + PYTHON3_CERT_FILE=$(dirname $0)/python/3.9.2-1_64bit/lib/python3.9/site-packages/certifi/cacert.pem + if [ ! -f $PYTHON3 ]; then + PYTHON3=$(dirname $0)/python/3.7.4-2_64bit/bin/python3 + PYTHON3_CERT_FILE=$(dirname $0)/python/3.7.4-2_64bit/lib/python3.7/site-packages/certifi/cacert.pem + fi + if [ -f $PYTHON3 ]; then + EMSDK_PYTHON=$PYTHON3 + + # When using our bundled python we never want the users + # PYTHONHOME or PYTHONPATH + # https://github.com/emscripten-core/emsdk/issues/598 + unset PYTHONHOME + unset PYTHONPATH + + # This is needed for MacOS. Without this, the urlopen + # code will try to use /usr/local/etc/openssl/cert.pem + # which may or may not exist on the system. + export SSL_CERT_FILE=$PYTHON3_CERT_FILE + fi +fi + +# If bundled python is not found, look for `python3` in PATH. This is especially important on macOS (See: +# https://github.com/emscripten-core/emsdk/pull/273) +if [ -z "$EMSDK_PYTHON" ]; then + PYTHON3=$(which python3 2> /dev/null) + if [ $? = 0 ]; then + EMSDK_PYTHON=$PYTHON3 + fi +fi + +# Finally fall back to just looking for `python` in PATH +if [ -z "$EMSDK_PYTHON" ]; then + EMSDK_PYTHON=python +fi + +exec "$EMSDK_PYTHON" "$0.py" "$@" diff --git a/emsdk.bat b/emsdk.bat index 6224d0de53..70e6b709b5 100644 --- a/emsdk.bat +++ b/emsdk.bat @@ -1,46 +1,52 @@ +@echo off + :: Find python from an explicit location relative to the Emscripten SDK. -@IF EXIST "%~dp0python\2.7.13.1_64bit\python-2.7.13.amd64\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.13.1_64bit\python-2.7.13.amd64\python.exe" - @GOTO end -) -@IF EXIST "%~dp0python\2.7.13.1_32bit\python-2.7.13\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.13.1_32bit\python-2.7.13\python.exe" - @GOTO end -) +setlocal -@IF EXIST "%~dp0python\2.7.5.3_64bit\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.5.3_64bit\python.exe" - @GOTO end +:: When using our bundled python we never want the users +:: PYTHONHOME or PYTHONPATH +:: https://github.com/emscripten-core/emsdk/issues/598 +if exist "%~dp0python\3.9.2-1_64bit\python.exe" ( + set EMSDK_PY="%~dp0python\3.9.2-1_64bit\python.exe" + set PYTHONHOME= + set PYTHONPATH= + goto end ) -@IF EXIST "%~dp0python\2.7.5.3_32bit\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.5.3_32bit\python.exe" - @GOTO end +if exist "%~dp0python\3.7.4-pywin32_64bit\python.exe" ( + set EMSDK_PY="%~dp0python\3.7.4-pywin32_64bit\python.exe" + set PYTHONHOME= + set PYTHONPATH= + goto end ) -@IF EXIST "%~dp0python\2.7.5_64bit\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.5_64bit\python.exe" - @GOTO end +if exist "%~dp0python\3.7.4_64bit\python.exe" ( + set EMSDK_PY="%~dp0python\3.7.4_64bit\python.exe" + set PYTHONHOME= + set PYTHONPATH= + goto end ) -@IF EXIST "%~dp0python\2.7.5.1_32bit\python.exe" ( - @SET EMSDK_PY="%~dp0python\2.7.5.1_32bit\python.exe" - @GOTO end +if exist "%~dp0python\2.7.13.1_64bit\python-2.7.13.amd64\python.exe" ( + set EMSDK_PY="%~dp0python\2.7.13.1_64bit\python-2.7.13.amd64\python.exe" + set PYTHONHOME= + set PYTHONPATH= + goto end ) :: As a last resort, access from PATH. -@SET EMSDK_PY=python +set EMSDK_PY=python :end -@call %EMSDK_PY% "%~dp0\emsdk" %* +call %EMSDK_PY% "%~dp0\emsdk.py" %* -@set EMSDK_PY= +endlocal -:: python is not able to set environment variables to the parent calling process, so -:: therefore have it craft a .bat file, which we invoke after finishing python execution, -:: to set up the environment variables -@IF EXIST emsdk_set_env.bat ( - @CALL emsdk_set_env.bat > NUL - @DEL /F /Q emsdk_set_env.bat +:: python is not able to set environment variables to the parent calling +:: process, so therefore have it craft a .bat file, which we invoke after +:: finishing python execution, to set up the environment variables +if exist "%~dp0\emsdk_set_env.bat" ( + call "%~dp0\emsdk_set_env.bat" > nul + del /F /Q "%~dp0\emsdk_set_env.bat" ) diff --git a/emsdk.ps1 b/emsdk.ps1 index 22f1797362..f9ec03655a 100644 --- a/emsdk.ps1 +++ b/emsdk.ps1 @@ -1,6 +1,9 @@ $ScriptDirectory = Split-Path -parent $PSCommandPath $PythonLocations = $( + "python\3.9.2-1_64bit\python.exe", + "python\3.7.4-pywin32_64bit\python.exe", + "python\3.7.4_64bit\python.exe", "python\2.7.13.1_64bit\python-2.7.13.amd64\python.exe", "python\2.7.13.1_32bit\python-2.7.13\python.exe", "python\2.7.5.3_64bit\python.exe", @@ -26,14 +29,14 @@ if (-Not $EMSDK_PY) { # Tell EMSDK to create environment variable setter as a .ps1 file $env:EMSDK_POWERSHELL = 1 -& $EMSDK_PY "$ScriptDirectory/emsdk" $args +& $EMSDK_PY "$ScriptDirectory/emsdk.py" $args # python is not able to set environment variables to the parent calling process, so # therefore have it craft a .ps1 file, which we invoke after finishing python execution, # to set up the environment variables -if (Test-Path ./emsdk_set_env.ps1) { - & ./emsdk_set_env.ps1 - Remove-Item ./emsdk_set_env.ps1 +if (Test-Path $ScriptDirectory/emsdk_set_env.ps1) { + & $ScriptDirectory/emsdk_set_env.ps1 + Remove-Item $ScriptDirectory/emsdk_set_env.ps1 } Remove-Item Env:\EMSDK_POWERSHELL diff --git a/emsdk.py b/emsdk.py new file mode 100644 index 0000000000..657d1d57e5 --- /dev/null +++ b/emsdk.py @@ -0,0 +1,3256 @@ +#!/usr/bin/env python +# Copyright 2019 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +from __future__ import print_function + +import copy +from collections import OrderedDict +import errno +import json +import multiprocessing +import os +import os.path +import platform +import re +import shutil +import stat +import subprocess +import sys +import sysconfig +import zipfile +if os.name == 'nt': + try: + import winreg + except ImportError: + # old python 2 name + import _winreg as winreg + import ctypes.wintypes + +if sys.version_info >= (3,): + from urllib.parse import urljoin + from urllib.request import urlopen + import functools +else: + from urlparse import urljoin + from urllib2 import urlopen + + +emsdk_packages_url = 'https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/' + +emscripten_releases_repo = 'https://chromium.googlesource.com/emscripten-releases' + +emscripten_releases_download_url_template = "https://storage.googleapis.com/webassembly/emscripten-releases-builds/%s/%s/wasm-binaries.%s" + +# This was previously `master.zip` but we are transitioning to `main` and +# `HEAD.zip` works for both cases. In future we could switch this to +# `main.zip` perhaps. +emsdk_zip_download_url = 'https://github.com/emscripten-core/emsdk/archive/HEAD.zip' + +zips_subdir = 'zips/' + +extra_release_tag = None + +# Enable this to do very verbose printing about the different steps that are +# being run. Useful for debugging. +VERBOSE = int(os.getenv('EMSDK_VERBOSE', '0')) +TTY_OUTPUT = not os.getenv('EMSDK_NOTTY', not sys.stdout.isatty()) + +WINDOWS = False +if os.name == 'nt' or (os.getenv('SYSTEMROOT') is not None and 'windows' in os.getenv('SYSTEMROOT').lower()) or (os.getenv('COMSPEC') is not None and 'windows' in os.getenv('COMSPEC').lower()): + WINDOWS = True + + +def errlog(msg): + print(msg, file=sys.stderr) + + +MINGW = False +MSYS = False +if os.getenv('MSYSTEM'): + MSYS = True + # Some functions like os.path.normpath() exhibit different behavior between + # different versions of Python, so we need to distinguish between the MinGW + # and MSYS versions of Python + if sysconfig.get_platform() == 'mingw': + MINGW = True + if os.getenv('MSYSTEM') != 'MSYS' and os.getenv('MSYSTEM') != 'MINGW64': + # https://stackoverflow.com/questions/37460073/msys-vs-mingw-internal-environment-variables + errlog('Warning: MSYSTEM environment variable is present, and is set to "' + os.getenv('MSYSTEM') + '". This shell has not been tested with emsdk and may not work.') + +MACOS = False +if platform.mac_ver()[0] != '': + MACOS = True + +LINUX = False +if not MACOS and (platform.system() == 'Linux'): + LINUX = True + +UNIX = (MACOS or LINUX) + + +# Pick which shell of 4 shells to use +POWERSHELL = bool(os.getenv('EMSDK_POWERSHELL')) +CSH = bool(os.getenv('EMSDK_CSH')) +CMD = bool(os.getenv('EMSDK_CMD')) +BASH = bool(os.getenv('EMSDK_BASH')) +if WINDOWS and BASH: + MSYS = True + +if not CSH and not POWERSHELL and not BASH and not CMD: + # Fall back to default of `cmd` on windows and `bash` otherwise + if WINDOWS and not MSYS: + CMD = True + else: + BASH = True + +if WINDOWS: + ENVPATH_SEPARATOR = ';' +else: + ENVPATH_SEPARATOR = ':' + +ARCH = 'unknown' +# platform.machine() may return AMD64 on windows, so standardize the case. +machine = platform.machine().lower() +if machine.startswith('x64') or machine.startswith('amd64') or machine.startswith('x86_64'): + ARCH = 'x86_64' +elif machine.endswith('86'): + ARCH = 'x86' +elif machine.startswith('aarch64') or machine.lower().startswith('arm64'): + ARCH = 'aarch64' +elif platform.machine().startswith('arm'): + ARCH = 'arm' +else: + errlog("Warning: unknown machine architecture " + machine) + errlog() + +# Don't saturate all cores to not steal the whole system, but be aggressive. +CPU_CORES = int(os.environ.get('EMSDK_NUM_CORES', max(multiprocessing.cpu_count() - 1, 1))) + +CMAKE_BUILD_TYPE_OVERRIDE = None + +# If true, perform a --shallow clone of git. +GIT_CLONE_SHALLOW = False + +# If true, LLVM backend is built with tests enabled, and Binaryen is built with +# Visual Studio static analyzer enabled. +BUILD_FOR_TESTING = False + +# If 'auto', assertions are decided by the build type +# (Release&MinSizeRel=disabled, Debug&RelWithDebInfo=enabled) +# Other valid values are 'ON' and 'OFF' +ENABLE_LLVM_ASSERTIONS = 'auto' + + +def os_name(): + if WINDOWS: + return 'win' + elif LINUX: + return 'linux' + elif MACOS: + return 'macos' + else: + raise Exception('unknown OS') + + +def os_name_for_emscripten_releases(): + if WINDOWS: + return 'win' + elif LINUX: + return 'linux' + elif MACOS: + return 'mac' + else: + raise Exception('unknown OS') + + +def debug_print(msg): + if VERBOSE: + errlog(msg) + + +def to_unix_path(p): + return p.replace('\\', '/') + + +def emsdk_path(): + return to_unix_path(os.path.dirname(os.path.realpath(__file__))) + + +EMSDK_SET_ENV = "" +if POWERSHELL: + EMSDK_SET_ENV = os.path.join(emsdk_path(), 'emsdk_set_env.ps1') +else: + EMSDK_SET_ENV = os.path.join(emsdk_path(), 'emsdk_set_env.bat') + + +# Parses https://github.com/emscripten-core/emscripten/tree/d6aced8 to a pair (https://github.com/emscripten-core/emscripten, d6aced8) +def parse_github_url_and_refspec(url): + if not url: + return ('', '') + + if url.endswith(('/tree/', '/tree', '/commit/', '/commit')): + raise Exception('Malformed git URL and refspec ' + url + '!') + + if '/tree/' in url: + if url.endswith('/'): + raise Exception('Malformed git URL and refspec ' + url + '!') + return url.split('/tree/') + elif '/commit/' in url: + if url.endswith('/'): + raise Exception('Malformed git URL and refspec ' + url + '!') + return url.split('/commit/') + else: + return (url, 'main') # Assume the default branch is main in the absence of a refspec + + +ARCHIVE_SUFFIXES = ('zip', '.tar', '.gz', '.xz', '.tbz2', '.bz2') + + +# Finds the given executable 'program' in PATH. Operates like the Unix tool 'which'. +def which(program): + def is_exe(fpath): + return os.path.isfile(fpath) and (WINDOWS or os.access(fpath, os.X_OK)) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + if WINDOWS and '.' not in fname: + if is_exe(exe_file + '.exe'): + return exe_file + '.exe' + if is_exe(exe_file + '.cmd'): + return exe_file + '.cmd' + if is_exe(exe_file + '.bat'): + return exe_file + '.bat' + + return None + + +def vswhere(version): + try: + program_files = os.environ['ProgramFiles(x86)'] if 'ProgramFiles(x86)' in os.environ else os.environ['ProgramFiles'] + vswhere_path = os.path.join(program_files, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe') + output = json.loads(subprocess.check_output([vswhere_path, '-latest', '-version', '[%s.0,%s.0)' % (version, version + 1), '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', '-property', 'installationPath', '-format', 'json'])) + # Visual Studio 2017 Express is not included in the above search, and it + # does not have the VC.Tools.x86.x64 tool, so do a catch-all attempt as a + # fallback, to detect Express version. + if not output: + output = json.loads(subprocess.check_output([vswhere_path, '-latest', '-version', '[%s.0,%s.0)' % (version, version + 1), '-products', '*', '-property', 'installationPath', '-format', 'json'])) + if not output: + return '' + return str(output[0]['installationPath']) + except Exception: + return '' + + +def vs_filewhere(installation_path, platform, file): + try: + vcvarsall = os.path.join(installation_path, 'VC\\Auxiliary\\Build\\vcvarsall.bat') + env = subprocess.check_output('cmd /c "%s" %s & where %s' % (vcvarsall, platform, file)) + paths = [path[:-len(file)] for path in env.split('\r\n') if path.endswith(file)] + return paths[0] + except Exception: + return '' + + +CMAKE_GENERATOR = 'Unix Makefiles' +if WINDOWS: + # Detect which CMake generator to use when building on Windows + if '--mingw' in sys.argv: + CMAKE_GENERATOR = 'MinGW Makefiles' + elif '--vs2017' in sys.argv: + CMAKE_GENERATOR = 'Visual Studio 15' + elif '--vs2019' in sys.argv: + CMAKE_GENERATOR = 'Visual Studio 16' + else: + program_files = os.environ['ProgramFiles(x86)'] if 'ProgramFiles(x86)' in os.environ else os.environ['ProgramFiles'] + vs2019_exists = len(vswhere(16)) > 0 + vs2017_exists = len(vswhere(15)) > 0 + mingw_exists = which('mingw32-make') is not None and which('g++') is not None + if vs2019_exists: + CMAKE_GENERATOR = 'Visual Studio 16' + elif vs2017_exists: + # VS2017 has an LLVM build issue, see + # https://github.com/kripken/emscripten-fastcomp/issues/185 + CMAKE_GENERATOR = 'Visual Studio 15' + elif mingw_exists: + CMAKE_GENERATOR = 'MinGW Makefiles' + else: + # No detected generator + CMAKE_GENERATOR = '' + + +sys.argv = [a for a in sys.argv if a not in ('--mingw', '--vs2017', '--vs2019')] + + +# Computes a suitable path prefix to use when building with a given generator. +def cmake_generator_prefix(): + if CMAKE_GENERATOR == 'Visual Studio 16': + return '_vs2019' + if CMAKE_GENERATOR == 'Visual Studio 15': + return '_vs2017' + elif CMAKE_GENERATOR == 'MinGW Makefiles': + return '_mingw' + # Unix Makefiles do not specify a path prefix for backwards path compatibility + return '' + + +# Removes a directory tree even if it was readonly, and doesn't throw exception +# on failure. +def remove_tree(d): + debug_print('remove_tree(' + str(d) + ')') + if not os.path.exists(d): + return + try: + def remove_readonly_and_try_again(func, path, exc_info): + if not (os.stat(path).st_mode & stat.S_IWRITE): + os.chmod(path, stat.S_IWRITE) + func(path) + else: + raise + shutil.rmtree(d, onerror=remove_readonly_and_try_again) + except Exception as e: + debug_print('remove_tree threw an exception, ignoring: ' + str(e)) + + +def win_set_environment_variable_direct(key, value, system=True): + folder = None + try: + if system: + # Read globally from ALL USERS section. + folder = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', 0, winreg.KEY_ALL_ACCESS) + else: + # Register locally from CURRENT USER section. + folder = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_ALL_ACCESS) + winreg.SetValueEx(folder, key, 0, winreg.REG_EXPAND_SZ, value) + debug_print('Set key=' + key + ' with value ' + value + ' in registry.') + return True + except Exception as e: + # 'Access is denied.' + if e.args[3] == 5: + exit_with_error('Error! Failed to set the environment variable \'' + key + '\'! Setting environment variables permanently requires administrator access. Please rerun this command with administrative privileges. This can be done for example by holding down the Ctrl and Shift keys while opening a command prompt in start menu.') + errlog('Failed to write environment variable ' + key + ':') + errlog(str(e)) + return False + finally: + if folder is not None: + folder.Close() + + +def win_get_environment_variable(key, system=True, user=True, fallback=True): + if (not system and not user and fallback): + # if no --system or --permanent flag is provided use shell's value + return os.environ[key] + try: + folder = None + try: + if system: + # Read globally from ALL USERS section. + folder = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment') + else: + # Register locally from CURRENT USER section. + folder = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment') + value = str(winreg.QueryValueEx(folder, key)[0]) + except Exception: + # If reading registry fails for some reason - read via os.environ. This has the drawback + # that expansion items such as %PROGRAMFILES% will have been expanded, so + # need to be precise not to set these back to system registry, or + # expansion items would be lost. + if fallback: + return os.environ[key] + return None + finally: + if folder is not None: + folder.Close() + + except Exception as e: + # this catch is if both the registry key threw an exception and the key is not in os.environ + if e.args[0] != 2: + # 'The system cannot find the file specified.' + errlog('Failed to read environment variable ' + key + ':') + errlog(str(e)) + return None + return value + + +def win_set_environment_variable(key, value, system, user): + debug_print('set ' + str(key) + '=' + str(value) + ', in system=' + str(system)) + previous_value = win_get_environment_variable(key, system=system, user=user) + if previous_value == value: + debug_print(' no need to set, since same value already exists.') + # No need to elevate UAC for nothing to set the same value, skip. + return False + + if not value: + try: + if system: + cmd = ['REG', 'DELETE', 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', '/V', key, '/f'] + else: + cmd = ['REG', 'DELETE', 'HKCU\\Environment', '/V', key, '/f'] + debug_print(str(cmd)) + value = subprocess.call(cmd, stdout=subprocess.PIPE) + except Exception: + return False + return True + + try: + if win_set_environment_variable_direct(key, value, system): + return True + # Escape % signs so that we don't expand references to environment variables. + value = value.replace('%', '^%') + if len(value) >= 1024: + exit_with_error('ERROR! The new environment variable ' + key + ' is more than 1024 characters long! A value this long cannot be set via command line: please add the environment variable specified above to system environment manually via Control Panel.') + cmd = ['SETX', key, value] + debug_print(str(cmd)) + retcode = subprocess.call(cmd, stdout=subprocess.PIPE) + if retcode != 0: + errlog('ERROR! Failed to set environment variable ' + key + '=' + value + '. You may need to set it manually.') + else: + return True + except Exception as e: + errlog('ERROR! Failed to set environment variable ' + key + '=' + value + ':') + errlog(str(e)) + errlog('You may need to set it manually.') + + return False + + +def win_set_environment_variables(env_vars_to_add, system, user): + if not env_vars_to_add: + return + + changed = False + + for key, value in env_vars_to_add: + if win_set_environment_variable(key, value, system, user): + if not changed: + changed = True + print('Setting global environment variables:') + + print(key + ' = ' + value) + + if not changed: + print('Global environment variables up to date') + return + + # if changes were made then we need to notify other processes + try: + HWND_BROADCAST = ctypes.wintypes.HWND(0xFFFF) # win32con.HWND_BROADCAST == 65535 + WM_SETTINGCHANGE = 0x001A # win32con.WM_SETTINGCHANGE == 26 + SMTO_BLOCK = 0x0001 # win32con.SMTO_BLOCK == 1 + ctypes.windll.user32.SendMessageTimeoutA( + HWND_BROADCAST, # hWnd: notify everyone + WM_SETTINGCHANGE, # Msg: registry changed + 0, # wParam: Must be 0 when setting changed is sent by users + 'Environment', # lParam: Specifically environment variables changed + SMTO_BLOCK, # fuFlags: Wait for message to be sent or timeout + 100) # uTimeout: 100ms + except Exception as e: + errlog('SendMessageTimeout failed with error: ' + str(e)) + + +def win_delete_environment_variable(key, system=True, user=True): + debug_print('win_delete_environment_variable(key=' + key + ', system=' + str(system) + ')') + return win_set_environment_variable(key, None, system, user) + + +# Returns the absolute pathname to the given path inside the Emscripten SDK. +def sdk_path(path): + if os.path.isabs(path): + return path + + return to_unix_path(os.path.join(emsdk_path(), path)) + + +# Modifies the given file in-place to contain '\r\n' line endings. +def file_to_crlf(filename): + text = open(filename, 'r').read() + text = text.replace('\r\n', '\n').replace('\n', '\r\n') + open(filename, 'wb').write(text) + + +# Modifies the given file in-place to contain '\n' line endings. +def file_to_lf(filename): + text = open(filename, 'r').read() + text = text.replace('\r\n', '\n') + open(filename, 'wb').write(text) + + +# Removes a single file, suppressing exceptions on failure. +def rmfile(filename): + debug_print('rmfile(' + filename + ')') + try: + os.remove(filename) + except: + pass + + +def fix_lineendings(filename): + if WINDOWS: + file_to_crlf(filename) + else: + file_to_lf(filename) + + +# http://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python +def mkdir_p(path): + debug_print('mkdir_p(' + path + ')') + if os.path.exists(path): + return + try: + os.makedirs(path) + except OSError as exc: # Python >2.5 + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + + +def num_files_in_directory(path): + if not os.path.isdir(path): + return 0 + return len([name for name in os.listdir(path) if os.path.exists(os.path.join(path, name))]) + + +def run(cmd, cwd=None, quiet=False): + debug_print('run(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')') + process = subprocess.Popen(cmd, cwd=cwd, env=os.environ.copy()) + process.communicate() + if process.returncode != 0 and not quiet: + errlog(str(cmd) + ' failed with error code ' + str(process.returncode) + '!') + return process.returncode + + +# http://pythonicprose.blogspot.fi/2009/10/python-extract-targz-archive.html +def untargz(source_filename, dest_dir, unpack_even_if_exists=False): + debug_print('untargz(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')') + if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0: + print("File '" + source_filename + "' has already been unpacked, skipping.") + return True + print("Unpacking '" + source_filename + "' to '" + dest_dir + "'") + mkdir_p(dest_dir) + run(['tar', '-xvf' if VERBOSE else '-xf', sdk_path(source_filename), '--strip', '1'], cwd=dest_dir) + # tfile = tarfile.open(source_filename, 'r:gz') + # tfile.extractall(dest_dir) + return True + + +# On Windows, it is not possible to reference path names that are longer than +# ~260 characters, unless the path is referenced via a "\\?\" prefix. +# See https://msdn.microsoft.com/en-us/library/aa365247.aspx#maxpath and http://stackoverflow.com/questions/3555527/python-win32-filename-length-workaround +# In that mode, forward slashes cannot be used as delimiters. +def fix_potentially_long_windows_pathname(pathname): + if not WINDOWS: + return pathname + # Test if emsdk calls fix_potentially_long_windows_pathname() with long relative paths (which is problematic) + if not os.path.isabs(pathname) and len(pathname) > 200: + errlog('Warning: Seeing a relative path "' + pathname + '" which is dangerously long for being referenced as a short Windows path name. Refactor emsdk to be able to handle this!') + if pathname.startswith('\\\\?\\'): + return pathname + pathname = os.path.normpath(pathname.replace('/', '\\')) + if MINGW: + # MinGW versions of Python return normalized paths with backslashes + # converted to forward slashes, so we must use forward slashes in our + # prefix + return '//?/' + pathname + return '\\\\?\\' + pathname + + +# On windows, rename/move will fail if the destination exists, and there is no +# race-free way to do it. This method removes the destination if it exists, so +# the move always works +def move_with_overwrite(src, dest): + if os.path.exists(dest): + os.remove(dest) + os.rename(src, dest) + + +# http://stackoverflow.com/questions/12886768/simple-way-to-unzip-file-in-python-on-all-oses +def unzip(source_filename, dest_dir, unpack_even_if_exists=False): + debug_print('unzip(source_filename=' + source_filename + ', dest_dir=' + dest_dir + ')') + if not unpack_even_if_exists and num_files_in_directory(dest_dir) > 0: + print("File '" + source_filename + "' has already been unpacked, skipping.") + return True + print("Unpacking '" + source_filename + "' to '" + dest_dir + "'") + mkdir_p(dest_dir) + common_subdir = None + try: + with zipfile.ZipFile(source_filename) as zf: + # Implement '--strip 1' behavior to unzipping by testing if all the files + # in the zip reside in a common subdirectory, and if so, we move the + # output tree at the end of uncompression step. + for member in zf.infolist(): + words = member.filename.split('/') + if len(words) > 1: # If there is a directory component? + if common_subdir is None: + common_subdir = words[0] + elif common_subdir != words[0]: + common_subdir = None + break + else: + common_subdir = None + break + + unzip_to_dir = dest_dir + if common_subdir: + unzip_to_dir = os.path.join(os.path.dirname(dest_dir), 'unzip_temp') + + # Now do the actual decompress. + for member in zf.infolist(): + zf.extract(member, fix_potentially_long_windows_pathname(unzip_to_dir)) + dst_filename = os.path.join(unzip_to_dir, member.filename) + + # See: https://stackoverflow.com/questions/42326428/zipfile-in-python-file-permission + unix_attributes = member.external_attr >> 16 + if unix_attributes: + os.chmod(dst_filename, unix_attributes) + + # Move the extracted file to its final location without the base + # directory name, if we are stripping that away. + if common_subdir: + if not member.filename.startswith(common_subdir): + raise Exception('Unexpected filename "' + member.filename + '"!') + stripped_filename = '.' + member.filename[len(common_subdir):] + final_dst_filename = os.path.join(dest_dir, stripped_filename) + # Check if a directory + if stripped_filename.endswith('/'): + d = fix_potentially_long_windows_pathname(final_dst_filename) + if not os.path.isdir(d): + os.mkdir(d) + else: + parent_dir = os.path.dirname(fix_potentially_long_windows_pathname(final_dst_filename)) + if parent_dir and not os.path.exists(parent_dir): + os.makedirs(parent_dir) + move_with_overwrite(fix_potentially_long_windows_pathname(dst_filename), fix_potentially_long_windows_pathname(final_dst_filename)) + + if common_subdir: + remove_tree(unzip_to_dir) + except zipfile.BadZipfile as e: + errlog("Unzipping file '" + source_filename + "' failed due to reason: " + str(e) + "! Removing the corrupted zip file.") + rmfile(source_filename) + return False + except Exception as e: + errlog("Unzipping file '" + source_filename + "' failed due to reason: " + str(e)) + return False + + return True + + +# This function interprets whether the given string looks like a path to a +# directory instead of a file, without looking at the actual filesystem. +# 'a/b/c' points to directory, so does 'a/b/c/', but 'a/b/c.x' is parsed as a +# filename +def path_points_to_directory(path): + if path == '.': + return True + last_slash = max(path.rfind('/'), path.rfind('\\')) + last_dot = path.rfind('.') + no_suffix = last_dot < last_slash or last_dot == -1 + if no_suffix: + return True + suffix = path[last_dot:] + # Very simple logic for the only file suffixes used by emsdk downloader. Other + # suffixes, like 'clang-3.2' are treated as dirs. + if suffix in ('.exe', '.zip', '.txt'): + return False + else: + return True + + +def get_content_length(download): + try: + meta = download.info() + if hasattr(meta, "getheaders") and hasattr(meta.getheaders, "Content-Length"): + return int(meta.getheaders("Content-Length")[0]) + elif hasattr(download, "getheader") and download.getheader('Content-Length'): + return int(download.getheader('Content-Length')) + elif hasattr(meta, "getheader") and meta.getheader('Content-Length'): + return int(meta.getheader('Content-Length')) + except Exception: + pass + + return 0 + + +def get_download_target(url, dstpath, filename_prefix=''): + file_name = filename_prefix + url.split('/')[-1] + if path_points_to_directory(dstpath): + file_name = os.path.join(dstpath, file_name) + else: + file_name = dstpath + + # Treat all relative destination paths as relative to the SDK root directory, + # not the current working directory. + file_name = sdk_path(file_name) + + return file_name + + +# On success, returns the filename on the disk pointing to the destination file that was produced +# On failure, returns None. +def download_file(url, dstpath, download_even_if_exists=False, filename_prefix=''): + debug_print('download_file(url=' + url + ', dstpath=' + dstpath + ')') + file_name = get_download_target(url, dstpath, filename_prefix) + + if os.path.exists(file_name) and not download_even_if_exists: + print("File '" + file_name + "' already downloaded, skipping.") + return file_name + try: + u = urlopen(url) + mkdir_p(os.path.dirname(file_name)) + with open(file_name, 'wb') as f: + file_size = get_content_length(u) + if file_size > 0: + print("Downloading: %s from %s, %s Bytes" % (file_name, url, file_size)) + else: + print("Downloading: %s from %s" % (file_name, url)) + + file_size_dl = 0 + # Draw a progress bar 80 chars wide (in non-TTY mode) + progress_max = 80 - 4 + progress_shown = 0 + block_sz = 256 * 1024 + if not TTY_OUTPUT: + print(' [', end='') + while True: + buffer = u.read(block_sz) + if not buffer: + break + + file_size_dl += len(buffer) + f.write(buffer) + if file_size: + percent = file_size_dl * 100.0 / file_size + if TTY_OUTPUT: + status = r" %10d [%3.02f%%]" % (file_size_dl, percent) + print(status, end='\r') + else: + while progress_shown < progress_max * percent / 100: + print('-', end='') + sys.stdout.flush() + progress_shown += 1 + if not TTY_OUTPUT: + print(']') + sys.stdout.flush() + except Exception as e: + errlog("Error: Downloading URL '" + url + "': " + str(e)) + if "SSL: CERTIFICATE_VERIFY_FAILED" in str(e) or "urlopen error unknown url type: https" in str(e): + errlog("Warning: Possibly SSL/TLS issue. Update or install Python SSL root certificates (2048-bit or greater) supplied in Python folder or https://pypi.org/project/certifi/ and try again.") + rmfile(file_name) + return None + except KeyboardInterrupt: + rmfile(file_name) + exit_with_error("Aborted by User, exiting") + return file_name + + +def run_get_output(cmd, cwd=None): + debug_print('run_get_output(cmd=' + str(cmd) + ', cwd=' + str(cwd) + ')') + process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, env=os.environ.copy(), universal_newlines=True) + stdout, stderr = process.communicate() + return (process.returncode, stdout, stderr) + + +# must_succeed: If false, the search is performed silently without printing out +# errors if not found. Empty string is returned if git is not found. +# If true, the search is required to succeed, and the execution +# will terminate with sys.exit(1) if not found. +def GIT(must_succeed=True): + # The order in the following is important, and specifies the preferred order + # of using the git tools. Primarily use git from emsdk if installed. If not, + # use system git. + gits = ['git/1.9.4/bin/git.exe', which('git')] + for git in gits: + try: + ret, stdout, stderr = run_get_output([git, '--version']) + if ret == 0: + return git + except: + pass + if must_succeed: + if WINDOWS: + msg = "ERROR: git executable was not found. Please install it by typing 'emsdk install git-1.9.4', or alternatively by installing it manually from http://git-scm.com/downloads . If you install git manually, remember to add it to PATH" + elif MACOS: + msg = "ERROR: git executable was not found. Please install git for this operation! This can be done from http://git-scm.com/ , or by installing XCode and then the XCode Command Line Tools (see http://stackoverflow.com/questions/9329243/xcode-4-4-command-line-tools )" + elif LINUX: + msg = "ERROR: git executable was not found. Please install git for this operation! This can be probably be done using your package manager, see http://git-scm.com/book/en/Getting-Started-Installing-Git" + else: + msg = "ERROR: git executable was not found. Please install git for this operation!" + exit_with_error(msg) + # Not found + return '' + + +def git_repo_version(repo_path): + returncode, stdout, stderr = run_get_output([GIT(), 'log', '-n', '1', '--pretty="%aD %H"'], cwd=repo_path) + if returncode == 0: + return stdout.strip() + else: + return "" + + +def git_recent_commits(repo_path, n=20): + returncode, stdout, stderr = run_get_output([GIT(), 'log', '-n', str(n), '--pretty="%H"'], cwd=repo_path) + if returncode == 0: + return stdout.strip().replace('\r', '').replace('"', '').split('\n') + else: + return [] + + +def git_clone(url, dstpath): + debug_print('git_clone(url=' + url + ', dstpath=' + dstpath + ')') + if os.path.isdir(os.path.join(dstpath, '.git')): + debug_print("Repository '" + url + "' already cloned to directory '" + dstpath + "', skipping.") + return True + mkdir_p(dstpath) + git_clone_args = [] + if GIT_CLONE_SHALLOW: + git_clone_args += ['--depth', '1'] + print('Cloning from ' + url + '...') + return run([GIT(), 'clone'] + git_clone_args + [url, dstpath]) == 0 + + +def git_checkout_and_pull(repo_path, branch_or_tag): + debug_print('git_checkout_and_pull(repo_path=' + repo_path + ', branch/tag=' + branch_or_tag + ')') + ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path) + if ret != 0: + return False + try: + print("Fetching latest changes to the branch/tag '" + branch_or_tag + "' for '" + repo_path + "'...") + ret = run([GIT(), 'fetch', '--quiet', 'origin'], repo_path) + if ret != 0: + return False + # this line assumes that the user has not gone and manually messed with the + # repo and added new remotes to ambiguate the checkout. + ret = run([GIT(), 'checkout', '--quiet', branch_or_tag], repo_path) + if ret != 0: + return False + # Test if branch_or_tag is a branch, or if it is a tag that needs to be updated + target_is_tag = run([GIT(), 'symbolic-ref', '-q', 'HEAD'], repo_path, quiet=True) + if not target_is_tag: + # update branch to latest (not needed for tags) + # this line assumes that the user has not gone and made local changes to the repo + ret = run([GIT(), 'merge', '--ff-only', 'origin/' + branch_or_tag], repo_path) + if ret != 0: + return False + except: + errlog('git operation failed!') + return False + print("Successfully updated and checked out branch/tag '" + branch_or_tag + "' on repository '" + repo_path + "'") + print("Current repository version: " + git_repo_version(repo_path)) + return True + + +def git_clone_checkout_and_pull(url, dstpath, branch): + debug_print('git_clone_checkout_and_pull(url=' + url + ', dstpath=' + dstpath + ', branch=' + branch + ')') + success = git_clone(url, dstpath) + if not success: + return False + success = git_checkout_and_pull(dstpath, branch) + return success + + +# Each tool can have its own build type, or it can be overridden on the command +# line. +def decide_cmake_build_type(tool): + if CMAKE_BUILD_TYPE_OVERRIDE: + return CMAKE_BUILD_TYPE_OVERRIDE + else: + return tool.cmake_build_type + + +# The root directory of the build. +def llvm_build_dir(tool): + generator_suffix = '' + if CMAKE_GENERATOR == 'Visual Studio 15': + generator_suffix = '_vs2017' + elif CMAKE_GENERATOR == 'Visual Studio 16': + generator_suffix = '_vs2019' + elif CMAKE_GENERATOR == 'MinGW Makefiles': + generator_suffix = '_mingw' + + bitness_suffix = '_32' if tool.bitness == 32 else '_64' + + if hasattr(tool, 'git_branch'): + build_dir = 'build_' + tool.git_branch.replace(os.sep, '-') + generator_suffix + bitness_suffix + else: + build_dir = 'build_' + tool.version + generator_suffix + bitness_suffix + return build_dir + + +def exe_suffix(filename): + if WINDOWS and not filename.endswith('.exe'): + filename += '.exe' + return filename + + +# The directory where the binaries are produced. (relative to the installation +# root directory of the tool) +def fastcomp_build_bin_dir(tool): + build_dir = llvm_build_dir(tool) + if WINDOWS and 'Visual Studio' in CMAKE_GENERATOR: + old_llvm_bin_dir = os.path.join(build_dir, 'bin', decide_cmake_build_type(tool)) + + new_llvm_bin_dir = None + default_cmake_build_type = decide_cmake_build_type(tool) + cmake_build_types = [default_cmake_build_type, 'Release', 'RelWithDebInfo', 'MinSizeRel', 'Debug'] + for build_type in cmake_build_types: + d = os.path.join(build_dir, build_type, 'bin') + if os.path.isfile(os.path.join(tool.installation_path(), d, exe_suffix('clang'))): + new_llvm_bin_dir = d + break + + if new_llvm_bin_dir and os.path.exists(os.path.join(tool.installation_path(), new_llvm_bin_dir)): + return new_llvm_bin_dir + elif os.path.exists(os.path.join(tool.installation_path(), old_llvm_bin_dir)): + return old_llvm_bin_dir + return os.path.join(build_dir, default_cmake_build_type, 'bin') + else: + return os.path.join(build_dir, 'bin') + + +def build_env(generator): + build_env = os.environ.copy() + + # To work around a build issue with older Mac OS X builds, add -stdlib=libc++ to all builds. + # See https://groups.google.com/forum/#!topic/emscripten-discuss/5Or6QIzkqf0 + if MACOS: + build_env['CXXFLAGS'] = ((build_env['CXXFLAGS'] + ' ') if hasattr(build_env, 'CXXFLAGS') else '') + '-stdlib=libc++' + elif 'Visual Studio 15' in generator or 'Visual Studio 16' in generator: + if 'Visual Studio 16' in generator: + path = vswhere(16) + else: + path = vswhere(15) + + # Configuring CMake for Visual Studio needs and env. var VCTargetsPath to be present. + # How this is supposed to work is unfortunately very undocumented. See + # https://discourse.cmake.org/t/cmake-failed-to-get-the-value-of-vctargetspath-with-vs2019-16-7/1839/16 + # for some conversation. Try a couple of common paths if one of them would work. + # In the future as new versions of VS come out, we likely need to add new paths into this list. + if 'VCTargetsPath' not in build_env: + vctargets_paths = [ + os.path.join(path, 'MSBuild\\Microsoft\\VC\\v160\\'), + os.path.join(path, 'Common7\\IDE\\VC\\VCTargets') + ] + for p in vctargets_paths: + if os.path.isfile(os.path.join(p, 'Microsoft.Cpp.Default.props')): + debug_print('Set env. var VCTargetsPath=' + p + ' for CMake.') + build_env['VCTargetsPath'] = p + break + else: + debug_print('Searched path ' + p + ' as candidate for VCTargetsPath, not working.') + + if 'VCTargetsPath' not in build_env: + errlog('Unable to locate Visual Studio compiler installation for generator "' + generator + '"!') + errlog('Either rerun installation in Visual Studio Command Prompt, or locate directory to Microsoft.Cpp.Default.props manually') + sys.exit(1) + + # CMake and VS2017 cl.exe needs to have mspdb140.dll et al. in its PATH. + vc_bin_paths = [vs_filewhere(path, 'amd64', 'cl.exe'), + vs_filewhere(path, 'x86', 'cl.exe')] + for path in vc_bin_paths: + if os.path.isdir(path): + build_env['PATH'] = build_env['PATH'] + ';' + path + + return build_env + + +def get_generator_for_sln_file(sln_file): + contents = open(sln_file, 'r').read() + if '# Visual Studio 16' in contents or '# Visual Studio Version 16' in contents: # VS2019 + return 'Visual Studio 16' + if '# Visual Studio 15' in contents: # VS2017 + return 'Visual Studio 15' + raise Exception('Unknown generator used to build solution file ' + sln_file) + + +def find_msbuild(sln_file): + # The following logic attempts to find a Visual Studio version specific + # MSBuild.exe from a list of known locations. + generator = get_generator_for_sln_file(sln_file) + debug_print('find_msbuild looking for generator ' + str(generator)) + if generator == 'Visual Studio 16': # VS2019 + path = vswhere(16) + search_paths = [os.path.join(path, 'MSBuild/Current/Bin'), + os.path.join(path, 'MSBuild/15.0/Bin/amd64'), + os.path.join(path, 'MSBuild/15.0/Bin')] + elif generator == 'Visual Studio 15': # VS2017 + path = vswhere(15) + search_paths = [os.path.join(path, 'MSBuild/15.0/Bin/amd64'), + os.path.join(path, 'MSBuild/15.0/Bin')] + else: + raise Exception('Unknown generator!') + + for path in search_paths: + p = os.path.join(path, 'MSBuild.exe') + debug_print('Searching for MSBuild.exe: ' + p) + if os.path.isfile(p): + return p + debug_print('MSBuild.exe in PATH? ' + str(which('MSBuild.exe'))) + # Last fallback, try any MSBuild from PATH (might not be compatible, but best effort) + return which('MSBuild.exe') + + +def make_build(build_root, build_type, build_target_platform='x64'): + debug_print('make_build(build_root=' + build_root + ', build_type=' + build_type + ', build_target_platform=' + build_target_platform + ')') + if CPU_CORES > 1: + print('Performing a parallel build with ' + str(CPU_CORES) + ' cores.') + else: + print('Performing a singlethreaded build.') + + generator_to_use = CMAKE_GENERATOR + + if WINDOWS: + if 'Visual Studio' in CMAKE_GENERATOR: + solution_name = str(subprocess.check_output(['dir', '/b', '*.sln'], shell=True, cwd=build_root).decode('utf-8').strip()) + generator_to_use = get_generator_for_sln_file(os.path.join(build_root, solution_name)) + # Disabled for now: Don't pass /maxcpucount argument to msbuild, since it + # looks like when building, msbuild already automatically spawns the full + # amount of logical cores the system has, and passing the number of + # logical cores here has been observed to give a quadratic N*N explosion + # on the number of spawned processes (e.g. on a Core i7 5960X with 16 + # logical cores, it would spawn 16*16=256 cl.exe processes, which would + # start crashing when running out of system memory) + # make = [find_msbuild(os.path.join(build_root, solution_name)), '/maxcpucount:' + str(CPU_CORES), '/t:Build', '/p:Configuration=' + build_type, '/nologo', '/verbosity:minimal', solution_name] + make = [find_msbuild(os.path.join(build_root, solution_name)), '/t:Build', '/p:Configuration=' + build_type, '/p:Platform=' + build_target_platform, '/nologo', '/verbosity:minimal', solution_name] + else: + make = ['mingw32-make', '-j' + str(CPU_CORES)] + else: + make = ['cmake', '--build', '.', '--', '-j' + str(CPU_CORES)] + + # Build + try: + print('Running build: ' + str(make)) + ret = subprocess.check_call(make, cwd=build_root, env=build_env(generator_to_use)) + if ret != 0: + errlog('Build failed with exit code ' + ret + '!') + errlog('Working directory: ' + build_root) + return False + except Exception as e: + errlog('Build failed due to exception!') + errlog('Working directory: ' + build_root) + errlog(str(e)) + return False + + return True + + +def cmake_configure(generator, build_root, src_root, build_type, extra_cmake_args=[]): + debug_print('cmake_configure(generator=' + str(generator) + ', build_root=' + str(build_root) + ', src_root=' + str(src_root) + ', build_type=' + str(build_type) + ', extra_cmake_args=' + str(extra_cmake_args) + ')') + # Configure + if not os.path.isdir(build_root): + # Create build output directory if it doesn't yet exist. + os.mkdir(build_root) + try: + if generator: + generator = ['-G', generator] + else: + generator = [] + cmdline = ['cmake'] + generator + ['-DCMAKE_BUILD_TYPE=' + build_type, '-DPYTHON_EXECUTABLE=' + sys.executable] + extra_cmake_args + [src_root] + print('Running CMake: ' + str(cmdline)) + + def quote_parens(x): + if ' ' in x: + return '"' + x.replace('"', '\\"') + '"' + else: + return x + + # Create a file 'recmake.bat/sh' in the build root that user can call to + # manually recmake the build tree with the previous build params + open(os.path.join(build_root, 'recmake.' + ('bat' if WINDOWS else 'sh')), 'w').write(' '.join(map(quote_parens, cmdline))) + ret = subprocess.check_call(cmdline, cwd=build_root, env=build_env(CMAKE_GENERATOR)) + if ret != 0: + errlog('CMake invocation failed with exit code ' + ret + '!') + errlog('Working directory: ' + build_root) + return False + except OSError as e: + if e.errno == errno.ENOENT: + errlog(str(e)) + errlog('Could not run CMake, perhaps it has not been installed?') + if WINDOWS: + errlog('Installing this package requires CMake. Get it from http://www.cmake.org/') + elif LINUX: + errlog('Installing this package requires CMake. Get it via your system package manager (e.g. sudo apt-get install cmake), or from http://www.cmake.org/') + elif MACOS: + errlog('Installing this package requires CMake. Get it via a macOS package manager (Homebrew: "brew install cmake", or MacPorts: "sudo port install cmake"), or from http://www.cmake.org/') + return False + raise + except Exception as e: + errlog('CMake invocation failed due to exception!') + errlog('Working directory: ' + build_root) + errlog(str(e)) + return False + + return True + + +def xcode_sdk_version(): + try: + output = subprocess.check_output(['xcrun', '--show-sdk-version']) + if sys.version_info >= (3,): + output = output.decode('utf8') + return output.strip().split('.') + except: + return subprocess.checkplatform.mac_ver()[0].split('.') + + +def build_fastcomp(tool): + debug_print('build_fastcomp(' + str(tool) + ')') + fastcomp_root = tool.installation_path() + fastcomp_src_root = os.path.join(fastcomp_root, 'src') + # Does this tool want to be git cloned from github? + if hasattr(tool, 'git_branch'): + success = git_clone_checkout_and_pull(tool.download_url(), fastcomp_src_root, tool.git_branch) + if not success: + return False + if hasattr(tool, 'clang_url'): + clang_root = os.path.join(fastcomp_src_root, 'tools/clang') + success = git_clone_checkout_and_pull(tool.clang_url, clang_root, tool.git_branch) + if not success: + return False + if hasattr(tool, 'lld_url'): + lld_root = os.path.join(fastcomp_src_root, 'tools/lld') + success = git_clone_checkout_and_pull(tool.lld_url, lld_root, tool.git_branch) + if not success: + return False + else: + # Not a git cloned tool, so instead download from git tagged releases + success = download_and_unzip(tool.download_url(), fastcomp_src_root, filename_prefix='llvm-e') + if not success: + return False + success = download_and_unzip(tool.windows_clang_url if WINDOWS else tool.unix_clang_url, os.path.join(fastcomp_src_root, 'tools/clang'), filename_prefix='clang-e') + if not success: + return False + + args = [] + + cmake_generator = CMAKE_GENERATOR + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + + build_dir = llvm_build_dir(tool) + build_root = os.path.join(fastcomp_root, build_dir) + + build_type = decide_cmake_build_type(tool) + + # Configure + tests_arg = 'ON' if BUILD_FOR_TESTING else 'OFF' + + enable_assertions = ENABLE_LLVM_ASSERTIONS.lower() == 'on' or (ENABLE_LLVM_ASSERTIONS == 'auto' and build_type.lower() != 'release' and build_type.lower() != 'minsizerel') + + only_supports_wasm = hasattr(tool, 'only_supports_wasm') + if ARCH == 'x86' or ARCH == 'x86_64': + targets_to_build = 'X86' + elif ARCH == 'arm': + targets_to_build = 'ARM' + elif ARCH == 'aarch64': + targets_to_build = 'AArch64' + else: + # May have problems with emconfigure + targets_to_build = '' + if not only_supports_wasm: + if targets_to_build != '': + targets_to_build += ';' + targets_to_build += 'JSBackend' + args += ['-DLLVM_TARGETS_TO_BUILD=' + targets_to_build, '-DLLVM_INCLUDE_EXAMPLES=OFF', '-DCLANG_INCLUDE_EXAMPLES=OFF', '-DLLVM_INCLUDE_TESTS=' + tests_arg, '-DCLANG_INCLUDE_TESTS=' + tests_arg, '-DLLVM_ENABLE_ASSERTIONS=' + ('ON' if enable_assertions else 'OFF')] + if os.environ.get('LLVM_CMAKE_ARGS'): + extra_args = os.environ['LLVM_CMAKE_ARGS'].split(',') + print('Passing the following extra arguments to LLVM CMake configuration: ' + str(extra_args)) + args += extra_args + + # MacOS < 10.13 workaround for LLVM build bug https://github.com/kripken/emscripten/issues/5418: + # specify HAVE_FUTIMENS=0 in the build if building with target SDK that is older than 10.13. + if MACOS and (not os.environ.get('LLVM_CMAKE_ARGS') or 'HAVE_FUTIMENS' not in os.environ.get('LLVM_CMAKE_ARGS')) and xcode_sdk_version() < ['10', '13']: + print('Passing -DHAVE_FUTIMENS=0 to LLVM CMake configure to workaround https://github.com/kripken/emscripten/issues/5418. Please update to macOS 10.13 or newer') + args += ['-DHAVE_FUTIMENS=0'] + + success = cmake_configure(cmake_generator, build_root, fastcomp_src_root, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + return success + + +# LLVM git source tree migrated to a single repository instead of multiple +# ones, build_llvm() builds via that repository structure +def build_llvm(tool): + debug_print('build_llvm(' + str(tool) + ')') + llvm_root = tool.installation_path() + llvm_src_root = os.path.join(llvm_root, 'src') + success = git_clone_checkout_and_pull(tool.download_url(), llvm_src_root, tool.git_branch) + if not success: + return False + + build_dir = llvm_build_dir(tool) + build_root = os.path.join(llvm_root, build_dir) + + build_type = decide_cmake_build_type(tool) + + # Configure + tests_arg = 'ON' if BUILD_FOR_TESTING else 'OFF' + + enable_assertions = ENABLE_LLVM_ASSERTIONS.lower() == 'on' or (ENABLE_LLVM_ASSERTIONS == 'auto' and build_type.lower() != 'release' and build_type.lower() != 'minsizerel') + + if ARCH == 'x86' or ARCH == 'x86_64': + targets_to_build = 'WebAssembly;X86' + elif ARCH == 'arm': + targets_to_build = 'WebAssembly;ARM' + elif ARCH == 'aarch64': + targets_to_build = 'WebAssembly;AArch64' + else: + targets_to_build = 'WebAssembly' + args = ['-DLLVM_TARGETS_TO_BUILD=' + targets_to_build, + '-DLLVM_INCLUDE_EXAMPLES=OFF', + '-DCLANG_INCLUDE_EXAMPLES=OFF', + '-DLLVM_INCLUDE_TESTS=' + tests_arg, + '-DCLANG_INCLUDE_TESTS=' + tests_arg, + '-DLLVM_ENABLE_ASSERTIONS=' + ('ON' if enable_assertions else 'OFF')] + # LLVM build system bug: looks like everything needs to be passed to LLVM_ENABLE_PROJECTS twice. (or every second field is ignored?) + + # LLVM build system bug #2: compiler-rt does not build on Windows. It insists on performing a CMake install step that writes to C:\Program Files. Attempting + # to reroute that to build_root directory then fails on an error + # file INSTALL cannot find + # "C:/code/emsdk/llvm/git/build_master_vs2017_64/$(Configuration)/lib/clang/10.0.0/lib/windows/clang_rt.ubsan_standalone-x86_64.lib". + # (there instead of $(Configuration), one would need ${CMAKE_BUILD_TYPE} ?) + # It looks like compiler-rt is not compatible to build on Windows? + args += ['-DLLVM_ENABLE_PROJECTS="clang;clang;lld;lld"'] + cmake_generator = CMAKE_GENERATOR + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + args += ['-Thost=x64'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + args += ['-Thost=x64'] + + if os.environ.get('LLVM_CMAKE_ARGS'): + extra_args = os.environ['LLVM_CMAKE_ARGS'].split(',') + print('Passing the following extra arguments to LLVM CMake configuration: ' + str(extra_args)) + args += extra_args + + cmakelists_dir = os.path.join(llvm_src_root, 'llvm') + success = cmake_configure(cmake_generator, build_root, cmakelists_dir, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + return success + + +def build_ninja(tool): + debug_print('build_ninja(' + str(tool) + ')') + root = os.path.normpath(tool.installation_path()) + src_root = os.path.join(root, 'src') + success = git_clone_checkout_and_pull(tool.download_url(), src_root, tool.git_branch) + if not success: + return False + + build_dir = llvm_build_dir(tool) + build_root = os.path.join(root, build_dir) + + build_type = decide_cmake_build_type(tool) + + # Configure + cmake_generator = CMAKE_GENERATOR + args = [] + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + args += ['-Thost=x64'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + args += ['-Thost=x64'] + + cmakelists_dir = os.path.join(src_root) + success = cmake_configure(cmake_generator, build_root, cmakelists_dir, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + + if success: + bin_dir = os.path.join(root, 'bin') + mkdir_p(bin_dir) + exe_paths = [os.path.join(build_root, 'Release', 'ninja'), os.path.join(build_root, 'ninja')] + for e in exe_paths: + for s in ['.exe', '']: + ninja = e + s + if os.path.isfile(ninja): + dst = os.path.join(bin_dir, 'ninja' + s) + shutil.copyfile(ninja, dst) + os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC) + + return success + + +def build_ccache(tool): + debug_print('build_ccache(' + str(tool) + ')') + root = os.path.normpath(tool.installation_path()) + src_root = os.path.join(root, 'src') + success = git_clone_checkout_and_pull(tool.download_url(), src_root, tool.git_branch) + if not success: + return False + + build_dir = llvm_build_dir(tool) + build_root = os.path.join(root, build_dir) + + build_type = decide_cmake_build_type(tool) + + # Configure + cmake_generator = CMAKE_GENERATOR + args = ['-DZSTD_FROM_INTERNET=ON'] + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + args += ['-Thost=x64'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + args += ['-Thost=x64'] + + cmakelists_dir = os.path.join(src_root) + success = cmake_configure(cmake_generator, build_root, cmakelists_dir, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + + if success: + bin_dir = os.path.join(root, 'bin') + mkdir_p(bin_dir) + exe_paths = [os.path.join(build_root, 'Release', 'ccache'), os.path.join(build_root, 'ccache')] + for e in exe_paths: + for s in ['.exe', '']: + ccache = e + s + if os.path.isfile(ccache): + dst = os.path.join(bin_dir, 'ccache' + s) + shutil.copyfile(ccache, dst) + os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC) + + cache_dir = os.path.join(root, 'cache') + open(os.path.join(root, 'emcc_ccache.conf'), 'w').write('''# Set maximum cache size to 10 GB: +max_size = 10G +cache_dir = %s +''' % cache_dir) + mkdir_p(cache_dir) + + return success + + +# Emscripten asm.js optimizer build scripts: +def optimizer_build_root(tool): + build_root = tool.installation_path().strip() + if build_root.endswith('/') or build_root.endswith('\\'): + build_root = build_root[:-1] + generator_prefix = cmake_generator_prefix() + build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_optimizer' + return build_root + + +def uninstall_optimizer(tool): + debug_print('uninstall_optimizer(' + str(tool) + ')') + build_root = optimizer_build_root(tool) + print("Deleting path '" + build_root + "'") + remove_tree(build_root) + + +def is_optimizer_installed(tool): + build_root = optimizer_build_root(tool) + return os.path.exists(build_root) + + +# Finds the newest installed version of a given tool +def find_latest_installed_tool(name): + for t in reversed(tools): + if t.id == name and t.is_installed(): + return t + + +# npm install in Emscripten root directory +def emscripten_npm_install(tool, directory): + node_tool = find_latest_installed_tool('node') + if not node_tool: + npm_fallback = which('npm') + if not npm_fallback: + errlog('Failed to find npm command!') + errlog('Running "npm ci" in installed Emscripten root directory ' + tool.installation_path() + ' is required!') + errlog('Please install node.js first!') + return False + node_path = os.path.dirname(npm_fallback) + else: + node_path = os.path.join(node_tool.installation_path(), 'bin') + + npm = os.path.join(node_path, 'npm' + ('.cmd' if WINDOWS else '')) + env = os.environ.copy() + env["PATH"] = node_path + os.pathsep + env["PATH"] + print('Running post-install step: npm ci ...') + try: + subprocess.check_output( + [npm, 'ci', '--production', '--no-optional'], + cwd=directory, stderr=subprocess.STDOUT, env=env, + universal_newlines=True) + except subprocess.CalledProcessError as e: + errlog('Error running %s:\n%s' % (e.cmd, e.output)) + return False + + print('Done running: npm ci') + return True + + +def emscripten_post_install(tool): + debug_print('emscripten_post_install(' + str(tool) + ')') + src_root = os.path.join(tool.installation_path(), 'tools', 'optimizer') + build_root = optimizer_build_root(tool) + build_type = decide_cmake_build_type(tool) + + args = [] + + # Configure + cmake_generator = CMAKE_GENERATOR + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + + success = cmake_configure(cmake_generator, build_root, src_root, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + if not success: + return False + + success = emscripten_npm_install(tool, tool.installation_path()) + + return True + + +# Binaryen build scripts: +def binaryen_build_root(tool): + build_root = tool.installation_path().strip() + if build_root.endswith('/') or build_root.endswith('\\'): + build_root = build_root[:-1] + generator_prefix = cmake_generator_prefix() + build_root = build_root + generator_prefix + '_' + str(tool.bitness) + 'bit_binaryen' + return build_root + + +def uninstall_binaryen(tool): + debug_print('uninstall_binaryen(' + str(tool) + ')') + build_root = binaryen_build_root(tool) + print("Deleting path '" + build_root + "'") + remove_tree(build_root) + + +def is_binaryen_installed(tool): + build_root = binaryen_build_root(tool) + return os.path.exists(build_root) + + +def build_binaryen_tool(tool): + debug_print('build_binaryen_tool(' + str(tool) + ')') + src_root = tool.installation_path() + build_root = binaryen_build_root(tool) + build_type = decide_cmake_build_type(tool) + + # Configure + args = [] + + cmake_generator = CMAKE_GENERATOR + if 'Visual Studio 16' in CMAKE_GENERATOR: # VS2019 + # With Visual Studio 16 2019, CMake changed the way they specify target arch. + # Instead of appending it into the CMake generator line, it is specified + # with a -A arch parameter. + args += ['-A', 'x64' if tool.bitness == 64 else 'x86'] + elif 'Visual Studio' in CMAKE_GENERATOR and tool.bitness == 64: + cmake_generator += ' Win64' + + if 'Visual Studio' in CMAKE_GENERATOR: + if BUILD_FOR_TESTING: + args += ['-DRUN_STATIC_ANALYZER=1'] + + success = cmake_configure(cmake_generator, build_root, src_root, build_type, args) + if not success: + return False + + # Make + success = make_build(build_root, build_type, 'x64' if tool.bitness == 64 else 'Win32') + + # Deploy scripts needed from source repository to build directory + remove_tree(os.path.join(build_root, 'scripts')) + shutil.copytree(os.path.join(src_root, 'scripts'), os.path.join(build_root, 'scripts')) + remove_tree(os.path.join(build_root, 'src', 'js')) + shutil.copytree(os.path.join(src_root, 'src', 'js'), os.path.join(build_root, 'src', 'js')) + + return success + + +def download_and_unzip(zipfile, dest_dir, download_even_if_exists=False, + filename_prefix='', clobber=True): + debug_print('download_and_unzip(zipfile=' + zipfile + ', dest_dir=' + dest_dir + ')') + + url = urljoin(emsdk_packages_url, zipfile) + download_target = get_download_target(url, zips_subdir, filename_prefix) + + # If the archive was already downloaded, and the directory it would be + # unpacked to has contents, assume it's the same contents and skip. + if not download_even_if_exists and num_files_in_directory(dest_dir) > 0: + print("The contents of file '" + zipfile + "' already exist in destination '" + dest_dir + "', skipping.") + return True + # Otherwise, if the archive must be downloaded, always write into the + # target directory, since it may be a new version of a tool that gets + # installed to the same place (that is, a different download name + # indicates different contents). + download_even_if_exists = True + + received_download_target = download_file(url, zips_subdir, download_even_if_exists, filename_prefix) + if not received_download_target: + return False + assert received_download_target == download_target + + # Remove the old directory, since we have some SDKs that install into the + # same directory. If we didn't do this contents of the previous install + # could remain. + if clobber: + remove_tree(dest_dir) + if zipfile.endswith('.zip'): + return unzip(download_target, dest_dir, unpack_even_if_exists=download_even_if_exists) + else: + return untargz(download_target, dest_dir, unpack_even_if_exists=download_even_if_exists) + + +def to_native_path(p): + if WINDOWS and not MSYS: + return to_unix_path(p).replace('/', '\\') + else: + return to_unix_path(p) + + +# Finds and returns a list of the directories that need to be added to PATH for +# the given set of tools. +def get_required_path(active_tools): + path_add = [to_native_path(emsdk_path())] + for tool in active_tools: + if hasattr(tool, 'activated_path'): + path = to_native_path(tool.expand_vars(tool.activated_path)) + path_add.append(path) + return path_add + + +# Returns the absolute path to the file '.emscripten' for the current user on +# this system. +def dot_emscripten_path(): + return os.path.join(emsdk_path(), ".emscripten") + + +dot_emscripten = {} + + +def parse_key_value(line): + if not line: + return ('', '') + eq = line.find('=') + if eq != -1: + key = line[0:eq].strip() + value = line[eq + 1:].strip() + return (key, value) + else: + return (key, '') + + +def load_dot_emscripten(): + dot_emscripten.clear() + lines = [] + try: + lines = open(dot_emscripten_path(), "r").read().split('\n') + except: + pass + for line in lines: + try: + key, value = parse_key_value(line) + if value != '': + dot_emscripten[key] = value + except: + pass + + +def generate_dot_emscripten(active_tools): + cfg = 'import os\n' + cfg += "emsdk_path = os.path.dirname(os.environ.get('EM_CONFIG')).replace('\\\\', '/')\n" + + # Different tools may provide the same activated configs; the latest to be + # activated is the relevant one. + activated_config = OrderedDict() + for tool in active_tools: + for name, value in tool.activated_config().items(): + activated_config[name] = value + + if 'NODE_JS' not in activated_config: + node_fallback = which('nodejs') + if not node_fallback: + node_fallback = 'node' + activated_config['NODE_JS'] = node_fallback + + for name, value in activated_config.items(): + cfg += name + " = '" + value + "'\n" + + cfg += '''\ +COMPILER_ENGINE = NODE_JS +JS_ENGINES = [NODE_JS] +''' + + cfg = cfg.replace("'" + emsdk_path(), "emsdk_path + '") + + if os.path.exists(dot_emscripten_path()): + backup_path = dot_emscripten_path() + ".old" + move_with_overwrite(dot_emscripten_path(), backup_path) + + with open(dot_emscripten_path(), "w") as text_file: + text_file.write(cfg) + + # Clear old emscripten content. + rmfile(os.path.join(emsdk_path(), ".emscripten_sanity")) + + path_add = get_required_path(active_tools) + if not WINDOWS: + emsdk_env = sdk_path('emsdk_env.sh') + print('Next steps:') + print('- To conveniently access emsdk tools from the command line,') + print(' consider adding the following directories to your PATH:') + for p in path_add: + print(' ' + p) + print('- This can be done for the current shell by running:') + print(' source "%s"' % emsdk_env) + print('- Configure emsdk in your bash profile by running:') + print(' echo \'source "%s"\' >> $HOME/.bash_profile' % emsdk_env) + + +def find_msbuild_dir(): + if 'ProgramFiles' in os.environ and os.environ['ProgramFiles']: + program_files = os.environ['ProgramFiles'] + else: + program_files = 'C:/Program Files' + if 'ProgramFiles(x86)' in os.environ and os.environ['ProgramFiles(x86)']: + program_files_x86 = os.environ['ProgramFiles(x86)'] + else: + program_files_x86 = 'C:/Program Files (x86)' + MSBUILDX86_DIR = os.path.join(program_files_x86, "MSBuild/Microsoft.Cpp/v4.0/Platforms") + MSBUILD_DIR = os.path.join(program_files, "MSBuild/Microsoft.Cpp/v4.0/Platforms") + if os.path.exists(MSBUILDX86_DIR): + return MSBUILDX86_DIR + if os.path.exists(MSBUILD_DIR): + return MSBUILD_DIR + # No MSbuild installed. + return '' + + +class Tool(object): + def __init__(self, data): + # Convert the dictionary representation of the tool in 'data' to members of + # this class for convenience. + for key, value in data.items(): + # Python2 compat, convert unicode to str + if sys.version_info < (3,) and isinstance(value, unicode): # noqa + value = value.encode('Latin-1') + setattr(self, key, value) + + # Cache the name ID of this Tool (these are read very often) + self.name = self.id + if self.version: + self.name += '-' + self.version + if hasattr(self, 'bitness'): + self.name += '-' + str(self.bitness) + 'bit' + + def __str__(self): + return self.name + + def __repr__(self): + return self.name + + def expand_vars(self, str): + if WINDOWS and '%MSBuildPlatformsDir%' in str: + str = str.replace('%MSBuildPlatformsDir%', find_msbuild_dir()) + if '%cmake_build_type_on_win%' in str: + str = str.replace('%cmake_build_type_on_win%', (decide_cmake_build_type(self) + '/') if WINDOWS else '') + if '%installation_dir%' in str: + str = str.replace('%installation_dir%', sdk_path(self.installation_dir())) + if '%generator_prefix%' in str: + str = str.replace('%generator_prefix%', cmake_generator_prefix()) + str = str.replace('%.exe%', '.exe' if WINDOWS else '') + if '%fastcomp_build_dir%' in str: + str = str.replace('%fastcomp_build_dir%', llvm_build_dir(self)) + if '%fastcomp_build_bin_dir%' in str: + str = str.replace('%fastcomp_build_bin_dir%', fastcomp_build_bin_dir(self)) + return str + + # Return true if this tool requires building from source, and false if this is a precompiled tool. + def needs_compilation(self): + if hasattr(self, 'cmake_build_type'): + return True + + if hasattr(self, 'uses'): + for tool_name in self.uses: + tool = find_tool(tool_name) + if not tool: + debug_print('Tool ' + str(self) + ' depends on ' + tool_name + ' which does not exist!') + continue + if tool.needs_compilation(): + return True + + return False + + # Specifies the target path where this tool will be installed to. This could + # either be a directory or a filename (e.g. in case of node.js) + def installation_path(self): + if WINDOWS and hasattr(self, 'windows_install_path'): + pth = self.expand_vars(self.windows_install_path) + return sdk_path(pth) + if hasattr(self, 'install_path'): + pth = self.expand_vars(self.install_path) + return sdk_path(pth) + p = self.version + if hasattr(self, 'bitness') and (not hasattr(self, 'append_bitness') or self.append_bitness): + p += '_' + str(self.bitness) + 'bit' + return sdk_path(os.path.join(self.id, p)) + + # Specifies the target directory this tool will be installed to. + def installation_dir(self): + dir = self.installation_path() + if path_points_to_directory(dir): + return dir + else: + return os.path.dirname(dir) + + # Returns the configuration item that needs to be added to .emscripten to make + # this Tool active for the current user. + def activated_config(self): + if not hasattr(self, 'activated_cfg'): + return {} + config = OrderedDict() + expanded = to_unix_path(self.expand_vars(self.activated_cfg)) + for specific_cfg in expanded.split(';'): + name, value = specific_cfg.split('=') + config[name] = value.strip("'") + return config + + def activated_environment(self): + if hasattr(self, 'activated_env'): + return self.expand_vars(self.activated_env).split(';') + else: + return [] + + def compatible_with_this_arch(self): + if hasattr(self, 'arch'): + if self.arch != ARCH: + return False + return True + + def compatible_with_this_os(self): + if hasattr(self, 'os'): + if self.os == 'all': + return True + if self.compatible_with_this_arch() and ((WINDOWS and 'win' in self.os) or (LINUX and ('linux' in self.os or 'unix' in self.os)) or (MACOS and ('macos' in self.os or 'unix' in self.os))): + return True + else: + return False + else: + if not hasattr(self, 'macos_url') and not hasattr(self, 'windows_url') and not hasattr(self, 'unix_url') and not hasattr(self, 'linux_url'): + return True + + if MACOS and hasattr(self, 'macos_url') and self.compatible_with_this_arch(): + return True + + if LINUX and hasattr(self, 'linux_url') and self.compatible_with_this_arch(): + return True + + if WINDOWS and (hasattr(self, 'windows_url') or hasattr(self, 'windows_install_path')) and self.compatible_with_this_arch(): + return True + + if UNIX and hasattr(self, 'unix_url'): + return True + + return hasattr(self, 'url') + + # the "version file" is a file inside install dirs that indicates the + # version installed there. this helps disambiguate when there is more than + # one version that may be installed to the same directory (which is used + # to avoid accumulating builds over time in some cases, with new builds + # overwriting the old) + def get_version_file_path(self): + return os.path.join(self.installation_path(), '.emsdk_version') + + def is_installed_version(self): + version_file_path = self.get_version_file_path() + if os.path.isfile(version_file_path): + with open(version_file_path, 'r') as version_file: + return version_file.read().strip() == self.name + return False + + def update_installed_version(self): + with open(self.get_version_file_path(), 'w') as version_file: + version_file.write(self.name + '\n') + return None + + def is_installed(self, skip_version_check=False): + # If this tool/sdk depends on other tools, require that all dependencies are + # installed for this tool to count as being installed. + if hasattr(self, 'uses'): + for tool_name in self.uses: + tool = find_tool(tool_name) + if tool is None: + errlog("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!") + return False + if not tool.is_installed(): + return False + + if self.download_url() is None: + # This tool does not contain downloadable elements, so it is installed by default. + return True + + content_exists = os.path.exists(self.installation_path()) and (os.path.isfile(self.installation_path()) or num_files_in_directory(self.installation_path()) > 0) + + # For e.g. fastcomp clang from git repo, the activated PATH is the + # directory where the compiler is built to, and installation_path is + # the directory where the source tree exists. To distinguish between + # multiple packages sharing the same source (clang-main-32bit, + # clang-main-64bit, clang-main-32bit and clang-main-64bit each + # share the same git repo), require that in addition to the installation + # directory, each item in the activated PATH must exist. + if hasattr(self, 'activated_path') and not os.path.exists(self.expand_vars(self.activated_path)): + content_exists = False + + if hasattr(self, 'custom_is_installed_script'): + if self.custom_is_installed_script == 'is_optimizer_installed': + return is_optimizer_installed(self) + elif self.custom_is_installed_script == 'is_binaryen_installed': + return is_binaryen_installed(self) + else: + raise Exception('Unknown custom_is_installed_script directive "' + self.custom_is_installed_script + '"!') + + return content_exists and (skip_version_check or self.is_installed_version()) + + def is_active(self): + if not self.is_installed(): + return False + + if self.id == 'vs-tool': + # vs-tool is a special tool since all versions must be installed to the + # same dir, which means that if this tool is installed, it is also active. + return True + + # All dependencies of this tool must be active as well. + deps = self.dependencies() + for tool in deps: + if not tool.is_active(): + return False + + activated_cfg = self.activated_config() + if not activated_cfg: + return len(deps) > 0 + + for key, value in activated_cfg.items(): + if key not in dot_emscripten: + debug_print(str(self) + ' is not active, because key="' + key + '" does not exist in .emscripten') + return False + + # all paths are stored dynamically relative to the emsdk root, so + # normalize those first. + dot_emscripten_key = dot_emscripten[key].replace("emsdk_path + '", "'" + emsdk_path()) + dot_emscripten_key = dot_emscripten_key.strip("'") + if dot_emscripten_key != value: + debug_print(str(self) + ' is not active, because key="' + key + '" has value "' + dot_emscripten_key + '" but should have value "' + value + '"') + return False + return True + + # Returns true if the system environment variables requires by this tool are currently active. + def is_env_active(self): + envs = self.activated_environment() + for env in envs: + key, value = parse_key_value(env) + if key not in os.environ or to_unix_path(os.environ[key]) != to_unix_path(value): + debug_print(str(self) + ' is not active, because environment variable key="' + key + '" has value "' + str(os.getenv(key)) + '" but should have value "' + value + '"') + return False + + if hasattr(self, 'activated_path'): + path = to_unix_path(self.expand_vars(self.activated_path)) + for p in path: + path_items = os.environ['PATH'].replace('\\', '/').split(ENVPATH_SEPARATOR) + if not normalized_contains(path_items, p): + debug_print(str(self) + ' is not active, because environment variable PATH item "' + p + '" is not present (PATH=' + os.environ['PATH'] + ')') + return False + return True + + # If this tool can be installed on this system, this function returns True. + # Otherwise, this function returns a string that describes the reason why this + # tool is not available. + def can_be_installed(self): + if hasattr(self, 'bitness'): + if self.bitness == 64 and not is_os_64bit(): + return "this tool is only provided for 64-bit OSes" + + if self.id == 'vs-tool': + msbuild_dir = find_msbuild_dir() + if msbuild_dir: + return True + else: + return "Visual Studio was not found!" + else: + return True + + def download_url(self): + if WINDOWS and hasattr(self, 'windows_url'): + return self.windows_url + elif MACOS and hasattr(self, 'macos_url'): + return self.macos_url + elif LINUX and hasattr(self, 'linux_url'): + return self.linux_url + elif UNIX and hasattr(self, 'unix_url'): + return self.unix_url + elif hasattr(self, 'url'): + return self.url + else: + return None + + def install(self): + """Returns True if the Tool was installed of False if was skipped due to + already being installed. + """ + if self.can_be_installed() is not True: + exit_with_error("The tool '" + str(self) + "' is not available due to the reason: " + self.can_be_installed()) + + if self.id == 'sdk': + return self.install_sdk() + else: + return self.install_tool() + + def install_sdk(self): + """Returns True if any SDK component was installed of False all componented + were already installed. + """ + print("Installing SDK '" + str(self) + "'..") + installed = False + + for tool_name in self.uses: + tool = find_tool(tool_name) + if tool is None: + exit_with_error("Manifest error: No tool by name '" + tool_name + "' found! This may indicate an internal SDK error!") + installed |= tool.install() + + if not installed: + print("All SDK components already installed: '" + str(self) + "'.") + return False + + if getattr(self, 'custom_install_script', None) == 'emscripten_npm_install': + # upstream tools have hardcoded paths that are not stored in emsdk_manifest.json registry + install_path = 'upstream' if 'releases-upstream' in self.version else 'fastcomp' + emscripten_dir = os.path.join(emsdk_path(), install_path, 'emscripten') + # Older versions of the sdk did not include the node_modules directory + # and require `npm ci` to be run post-install + if not os.path.exists(os.path.join(emscripten_dir, 'node_modules')): + if not emscripten_npm_install(self, emscripten_dir): + exit_with_error('post-install step failed: emscripten_npm_install') + + print("Done installing SDK '" + str(self) + "'.") + return True + + def install_tool(self): + """Returns True if the SDK was installed of False if was skipped due to + already being installed. + """ + # Avoid doing a redundant reinstall of the tool, if it has already been installed. + # However all tools that are sourced directly from git branches do need to be + # installed every time when requested, since the install step is then used to git + # pull the tool to a newer version. + if self.is_installed() and not hasattr(self, 'git_branch'): + print("Skipped installing " + self.name + ", already installed.") + return False + + print("Installing tool '" + str(self) + "'..") + url = self.download_url() + + if hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_fastcomp': + success = build_fastcomp(self) + elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_llvm': + success = build_llvm(self) + elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ninja': + success = build_ninja(self) + elif hasattr(self, 'custom_install_script') and self.custom_install_script == 'build_ccache': + success = build_ccache(self) + elif hasattr(self, 'git_branch'): + success = git_clone_checkout_and_pull(url, self.installation_path(), self.git_branch) + elif url.endswith(ARCHIVE_SUFFIXES): + # TODO: explain the vs-tool special-casing + download_even_if_exists = (self.id == 'vs-tool') + # The 'releases' sdk is doesn't include a verion number in the directory + # name and instead only one version can be install at the time and each + # one will clobber the other. This means we always need to extract this + # archive even when the target directory exists. + download_even_if_exists = (self.id == 'releases') + filename_prefix = getattr(self, 'zipfile_prefix', '') + success = download_and_unzip(url, self.installation_path(), download_even_if_exists=download_even_if_exists, filename_prefix=filename_prefix) + else: + dst_file = download_file(urljoin(emsdk_packages_url, self.download_url()), self.installation_path()) + if dst_file: + success = True + else: + success = False + + if not success: + exit_with_error("Installation failed!") + + if hasattr(self, 'custom_install_script'): + if self.custom_install_script == 'emscripten_post_install': + success = emscripten_post_install(self) + elif self.custom_install_script == 'emscripten_npm_install': + success = emscripten_npm_install(self, self.installation_path()) + elif self.custom_install_script in ('build_fastcomp', 'build_llvm', 'build_ninja', 'build_ccache'): + # 'build_fastcomp' is a special one that does the download on its + # own, others do the download manually. + pass + elif self.custom_install_script == 'build_binaryen': + success = build_binaryen_tool(self) + else: + raise Exception('Unknown custom_install_script command "' + self.custom_install_script + '"!') + + if not success: + exit_with_error("Installation failed!") + + # Install an emscripten-version.txt file if told to, and if there is one. + # (If this is not an actual release, but some other build, then we do not + # write anything.) + if hasattr(self, 'emscripten_releases_hash'): + emscripten_version_file_path = os.path.join(to_native_path(self.expand_vars(self.activated_path)), 'emscripten-version.txt') + version = get_emscripten_release_version(self.emscripten_releases_hash) + if version: + open(emscripten_version_file_path, 'w').write('"%s"' % version) + + print("Done installing tool '" + str(self) + "'.") + + # Sanity check that the installation succeeded, and if so, remove unneeded + # leftover installation files. + if not self.is_installed(skip_version_check=True): + exit_with_error("Installation of '" + str(self) + "' failed, but no error was detected. Either something went wrong with the installation, or this may indicate an internal emsdk error.") + + self.cleanup_temp_install_files() + self.update_installed_version() + return True + + def cleanup_temp_install_files(self): + url = self.download_url() + if url.endswith(ARCHIVE_SUFFIXES): + download_target = get_download_target(url, zips_subdir, getattr(self, 'zipfile_prefix', '')) + debug_print("Deleting temporary zip file " + download_target) + rmfile(download_target) + + def uninstall(self): + if not self.is_installed(): + print("Tool '" + str(self) + "' was not installed. No need to uninstall.") + return + print("Uninstalling tool '" + str(self) + "'..") + if hasattr(self, 'custom_uninstall_script'): + if self.custom_uninstall_script == 'uninstall_optimizer': + uninstall_optimizer(self) + elif self.custom_uninstall_script == 'uninstall_binaryen': + uninstall_binaryen(self) + else: + raise Exception('Unknown custom_uninstall_script directive "' + self.custom_uninstall_script + '"!') + print("Deleting path '" + self.installation_path() + "'") + remove_tree(self.installation_path()) + print("Done uninstalling '" + str(self) + "'.") + + def dependencies(self): + if not hasattr(self, 'uses'): + return [] + deps = [] + + for tool_name in self.uses: + tool = find_tool(tool_name) + if tool: + deps += [tool] + return deps + + def recursive_dependencies(self): + if not hasattr(self, 'uses'): + return [] + deps = [] + for tool_name in self.uses: + tool = find_tool(tool_name) + if tool: + deps += [tool] + deps += tool.recursive_dependencies() + return deps + + +# A global registry of all known Emscripten SDK tools available in the SDK manifest. +tools = [] +tools_map = {} + + +def add_tool(tool): + tool.is_sdk = False + tools.append(tool) + if find_tool(str(tool)): + raise Exception('Duplicate tool ' + str(tool) + '! Existing:\n{' + ', '.join("%s: %s" % item for item in vars(find_tool(str(tool))).items()) + '}, New:\n{' + ', '.join("%s: %s" % item for item in vars(tool).items()) + '}') + tools_map[str(tool)] = tool + + +# A global registry of all known SDK toolsets. +sdks = [] +sdks_map = {} + + +def add_sdk(sdk): + sdk.is_sdk = True + sdks.append(sdk) + if find_sdk(str(sdk)): + raise Exception('Duplicate sdk ' + str(sdk) + '! Existing:\n{' + ', '.join("%s: %s" % item for item in vars(find_sdk(str(sdk))).items()) + '}, New:\n{' + ', '.join("%s: %s" % item for item in vars(sdk).items()) + '}') + sdks_map[str(sdk)] = sdk + + +# N.B. In both tools and sdks list above, we take the convention that the newest +# items are at the back of the list (ascending chronological order) + +def find_tool(name): + return tools_map.get(name) + + +def find_sdk(name): + return sdks_map.get(name) + + +def is_os_64bit(): + # http://stackoverflow.com/questions/2208828/detect-64bit-os-windows-in-python + return platform.machine().endswith('64') + + +def find_latest_releases_version(): + releases_info = load_releases_info() + return releases_info['latest'] + + +def find_latest_releases_hash(): + releases_info = load_releases_info() + return releases_info['releases'][find_latest_releases_version()] + + +def find_latest_releases_sdk(which): + return 'sdk-releases-%s-%s-64bit' % (which, find_latest_releases_hash()) + + +def find_tot_sdk(): + debug_print('Fetching emscripten-releases repository...') + global extra_release_tag + extra_release_tag = get_emscripten_releases_tot() + return 'sdk-releases-upstream-%s-64bit' % (extra_release_tag) + + +def parse_emscripten_version(emscripten_root): + version_file = os.path.join(emscripten_root, 'emscripten-version.txt') + with open(version_file) as f: + version = f.read().strip() + version = version.strip('"').split('.') + return [int(v) for v in version] + + +# Given a git hash in emscripten-releases, find the emscripten +# version for it. There may not be one if this is not the hash of +# a release, in which case we return None. +def get_emscripten_release_version(emscripten_releases_hash): + releases_info = load_releases_info() + for key, value in dict(releases_info['releases']).items(): + if value == emscripten_releases_hash: + return key + return None + + +# Get the tip-of-tree build identifier. +def get_emscripten_releases_tot(): + git_clone_checkout_and_pull(emscripten_releases_repo, sdk_path('releases'), 'master') + recent_releases = git_recent_commits(sdk_path('releases')) + # The recent releases are the latest hashes in the git repo. There + # may not be a build for the most recent ones yet; find the last + # that does. + for release in recent_releases: + url = emscripten_releases_download_url_template % ( + os_name_for_emscripten_releases(), + release, + 'tbz2' if not WINDOWS else 'zip' + ) + try: + urlopen(url) + except: + continue + return release + exit_with_error('failed to find build of any recent emsdk revision') + + +def get_release_hash(arg, releases_info): + return releases_info.get(arg, None) or releases_info.get('sdk-' + arg + '-64bit') + + +def version_key(ver): + return tuple(map(int, re.split('[._-]', ver))) + + +# A sort function that is compatible with both Python 2 and Python 3 using a +# custom comparison function. +def python_2_3_sorted(arr, cmp): + if sys.version_info >= (3,): + return sorted(arr, key=functools.cmp_to_key(cmp)) + else: + return sorted(arr, cmp=cmp) + + +def is_emsdk_sourced_from_github(): + return os.path.exists(os.path.join(emsdk_path(), '.git')) + + +def update_emsdk(): + if is_emsdk_sourced_from_github(): + errlog('You seem to have bootstrapped Emscripten SDK by cloning from GitHub. In this case, use "git pull" instead of "emsdk update" to update emsdk. (Not doing that automatically in case you have local changes)') + sys.exit(1) + if not download_and_unzip(emsdk_zip_download_url, emsdk_path(), download_even_if_exists=True, clobber=False): + sys.exit(1) + + +# Lists all legacy (pre-emscripten-releases) tagged versions directly in the Git +# repositories. These we can pull and compile from source. +def load_legacy_emscripten_tags(): + return open(sdk_path('legacy-emscripten-tags.txt'), 'r').read().split('\n') + + +def load_legacy_binaryen_tags(): + return open(sdk_path('legacy-binaryen-tags.txt'), 'r').read().split('\n') + + +def remove_prefix(s, prefix): + if s.startswith(prefix): + return s[len(prefix):] + else: + return s + + +def remove_suffix(s, suffix): + if s.endswith(suffix): + return s[:len(s) - len(suffix)] + else: + return s + + +# filename should be one of: 'llvm-precompiled-tags-32bit.txt', 'llvm-precompiled-tags-64bit.txt' +def load_file_index_list(filename): + items = open(sdk_path(filename)).read().splitlines() + items = [remove_suffix(remove_suffix(remove_prefix(x, 'emscripten-llvm-e'), '.tar.gz'), '.zip').strip() for x in items] + items = [x for x in items if 'latest' not in x and len(x) > 0] + + # Sort versions from oldest to newest (the default sort would be + # lexicographic, i.e. '1.37.1 < 1.37.10 < 1.37.2') + return sorted(items, key=version_key) + + +def exit_with_error(msg): + errlog(str(msg)) + sys.exit(1) + + +# Load the json info for emscripten-releases. +def load_releases_info(): + if not hasattr(load_releases_info, 'cached_info'): + try: + text = open(sdk_path('emscripten-releases-tags.txt'), 'r').read() + load_releases_info.cached_info = json.loads(text) + except Exception as e: + print('Error parsing emscripten-releases-tags.txt!') + exit_with_error(str(e)) + + return load_releases_info.cached_info + + +def get_installed_sdk_version(): + version_file = sdk_path(os.path.join('upstream', '.emsdk_version')) + if not os.path.exists(version_file): + return None + with open(version_file) as f: + version = f.read() + return version.split('-')[2] + + +# Get a list of tags for emscripten-releases. +def load_releases_tags(): + tags = [] + tags_fastcomp = [] + info = load_releases_info() + + for version, sha in sorted(info['releases'].items(), key=lambda x: version_key(x[0])): + tags.append(sha) + # Only include versions older than 1.39.0 in fastcomp releases + if version_key(version) < (2, 0, 0): + tags_fastcomp.append(sha) + + if extra_release_tag: + tags.append(extra_release_tag) + + # Explicitly add the currently installed SDK version. This could be a custom + # version (installed explicitly) so it might not be part of the main list loaded above. + installed = get_installed_sdk_version() + if installed and installed not in tags: + tags.append(installed) + + return tags, tags_fastcomp + + +def load_releases_versions(): + info = load_releases_info() + versions = list(info['releases'].keys()) + return versions + + +def is_string(s): + if sys.version_info[0] >= 3: + return isinstance(s, str) + return isinstance(s, basestring) # noqa + + +def load_sdk_manifest(): + try: + manifest = json.loads(open(sdk_path("emsdk_manifest.json"), "r").read()) + except Exception as e: + print('Error parsing emsdk_manifest.json!') + print(str(e)) + return + + emscripten_tags = load_legacy_emscripten_tags() + llvm_precompiled_tags_32bit = [] + llvm_precompiled_tags_64bit = load_file_index_list('llvm-tags-64bit.txt') + llvm_precompiled_tags = llvm_precompiled_tags_32bit + llvm_precompiled_tags_64bit + binaryen_tags = load_legacy_binaryen_tags() + releases_tags, releases_tags_fastcomp = load_releases_tags() + + def dependencies_exist(sdk): + for tool_name in sdk.uses: + tool = find_tool(tool_name) + if not tool: + debug_print('missing dependency: ' + tool_name) + return False + return True + + def cmp_version(ver, cmp_operand, reference): + if cmp_operand == '<=': + return version_key(ver) <= version_key(reference) + if cmp_operand == '<': + return version_key(ver) < version_key(reference) + if cmp_operand == '>=': + return version_key(ver) >= version_key(reference) + if cmp_operand == '>': + return version_key(ver) > version_key(reference) + if cmp_operand == '==': + return version_key(ver) == version_key(reference) + if cmp_operand == '!=': + return version_key(ver) != version_key(reference) + raise Exception('Invalid cmp_operand "' + cmp_operand + '"!') + + def passes_filters(param, ver, filters): + for v in filters: + if v[0] == param and not cmp_version(ver, v[1], v[2]): + return False + return True + + # A 'category parameter' is a %foo%-encoded identifier that specifies + # a class of tools instead of just one tool, e.g. %tag% + def expand_category_param(param, category_list, t, is_sdk): + for i, ver in enumerate(category_list): + if not ver.strip(): + continue + t2 = copy.copy(t) + found_param = False + for p, v in vars(t2).items(): + if is_string(v) and param in v: + t2.__dict__[p] = v.replace(param, ver) + found_param = True + if not found_param: + continue + t2.is_old = i < len(category_list) - 2 + if hasattr(t2, 'uses'): + t2.uses = [x.replace(param, ver) for x in t2.uses] + + # Filter out expanded tools by version requirements, such as ["tag", "<=", "1.37.22"] + if hasattr(t2, 'version_filter'): + passes = passes_filters(param, ver, t2.version_filter) + if not passes: + continue + + if is_sdk: + if dependencies_exist(t2): + if not find_sdk(t2.name): + add_sdk(t2) + else: + debug_print('SDK ' + str(t2) + ' already existed in manifest, not adding twice') + else: + if not find_tool(t2.name): + add_tool(t2) + else: + debug_print('Tool ' + str(t2) + ' already existed in manifest, not adding twice') + + for tool in manifest['tools']: + t = Tool(tool) + if t.compatible_with_this_os(): + if not hasattr(t, 'is_old'): + t.is_old = False + + # Expand the metapackages that refer to tags + if '%tag%' in t.version: + expand_category_param('%tag%', emscripten_tags, t, is_sdk=False) + elif '%precompiled_tag%' in t.version: + expand_category_param('%precompiled_tag%', llvm_precompiled_tags, t, is_sdk=False) + elif '%precompiled_tag32%' in t.version: + expand_category_param('%precompiled_tag32%', llvm_precompiled_tags_32bit, t, is_sdk=False) + elif '%precompiled_tag64%' in t.version: + expand_category_param('%precompiled_tag64%', llvm_precompiled_tags_64bit, t, is_sdk=False) + elif '%binaryen_tag%' in t.version: + expand_category_param('%binaryen_tag%', binaryen_tags, t, is_sdk=False) + elif '%releases-tag%' in t.version and 'fastcomp' in t.version: + expand_category_param('%releases-tag%', releases_tags_fastcomp, t, is_sdk=False) + elif '%releases-tag%' in t.version: + expand_category_param('%releases-tag%', releases_tags, t, is_sdk=False) + else: + add_tool(t) + + for sdk_str in manifest['sdks']: + sdk_str['id'] = 'sdk' + sdk = Tool(sdk_str) + if sdk.compatible_with_this_os(): + if not hasattr(sdk, 'is_old'): + sdk.is_old = False + + if '%tag%' in sdk.version: + expand_category_param('%tag%', emscripten_tags, sdk, is_sdk=True) + elif '%precompiled_tag%' in sdk.version: + expand_category_param('%precompiled_tag%', llvm_precompiled_tags, sdk, is_sdk=True) + elif '%precompiled_tag32%' in sdk.version: + expand_category_param('%precompiled_tag32%', llvm_precompiled_tags_32bit, sdk, is_sdk=True) + elif '%precompiled_tag64%' in sdk.version: + expand_category_param('%precompiled_tag64%', llvm_precompiled_tags_64bit, sdk, is_sdk=True) + elif '%releases-tag%' in sdk.version and 'fastcomp' in sdk.version: + expand_category_param('%releases-tag%', releases_tags_fastcomp, sdk, is_sdk=True) + elif '%releases-tag%' in sdk.version: + expand_category_param('%releases-tag%', releases_tags, sdk, is_sdk=True) + else: + add_sdk(sdk) + + +# Tests if the two given tools can be active at the same time. +# Currently only a simple check for name for same tool with different versions, +# possibly adds more logic in the future. +def can_simultaneously_activate(tool1, tool2): + return tool1.id != tool2.id + + +def remove_nonexisting_tools(tool_list, log_errors=True): + i = 0 + while i < len(tool_list): + tool = tool_list[i] + if not tool.is_installed(): + if log_errors: + errlog("Warning: The SDK/tool '" + str(tool) + "' cannot be activated since it is not installed! Skipping this tool...") + tool_list.pop(i) + continue + i += 1 + return tool_list + + +# Expands dependencies for each tool, and removes ones that don't exist. +def process_tool_list(tools_to_activate, log_errors=True): + i = 0 + # Gather dependencies for each tool + while i < len(tools_to_activate): + tool = tools_to_activate[i] + deps = tool.recursive_dependencies() + tools_to_activate = tools_to_activate[:i] + deps + tools_to_activate[i:] + i += len(deps) + 1 + + tools_to_activate = remove_nonexisting_tools(tools_to_activate, log_errors=log_errors) + + # Remove conflicting tools + i = 0 + while i < len(tools_to_activate): + j = 0 + while j < i: + secondary_tool = tools_to_activate[j] + primary_tool = tools_to_activate[i] + if not can_simultaneously_activate(primary_tool, secondary_tool): + tools_to_activate.pop(j) + j -= 1 + i -= 1 + j += 1 + i += 1 + return tools_to_activate + + +def write_set_env_script(env_string): + assert(WINDOWS) + open(EMSDK_SET_ENV, 'w').write(env_string) + + +# Reconfigure .emscripten to choose the currently activated toolset, set PATH +# and other environment variables. +# Returns the full list of deduced tools that are now active. +def set_active_tools(tools_to_activate, permanently_activate, system): + tools_to_activate = process_tool_list(tools_to_activate, log_errors=True) + + if tools_to_activate: + tools = [x for x in tools_to_activate if not x.is_sdk] + print('Setting the following tools as active:\n ' + '\n '.join(map(lambda x: str(x), tools))) + print('') + + generate_dot_emscripten(tools_to_activate) + + # Construct a .bat script that will be invoked to set env. vars and PATH + # We only do this on windows since emsdk.bat is able to modify the + # calling shell environment. On other platform `source emsdk_env.sh` is + # required. + if WINDOWS: + # always set local environment variables since permanently activating will only set the registry settings and + # will not affect the current session + env_vars_to_add = get_env_vars_to_add(tools_to_activate, system, user=permanently_activate) + env_string = construct_env_with_vars(env_vars_to_add) + write_set_env_script(env_string) + + if permanently_activate: + win_set_environment_variables(env_vars_to_add, system, user=permanently_activate) + + return tools_to_activate + + +def currently_active_sdk(): + for sdk in reversed(sdks): + if sdk.is_active(): + return sdk + return None + + +def currently_active_tools(): + active_tools = [] + for tool in tools: + if tool.is_active(): + active_tools += [tool] + return active_tools + + +# http://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-python-whilst-preserving-order +def unique_items(seq): + seen = set() + seen_add = seen.add + return [x for x in seq if x not in seen and not seen_add(x)] + + +# Tests if a path is contained in the given list, but with separators normalized. +def normalized_contains(lst, elem): + elem = to_unix_path(elem) + for e in lst: + if elem == to_unix_path(e): + return True + return False + + +def to_msys_path(p): + p = to_unix_path(p) + new_path = re.sub(r'([a-zA-Z]):/(.*)', r'/\1/\2', p) + if len(new_path) > 3 and new_path[0] == '/' and new_path[2] == '/': + new_path = new_path[0] + new_path[1].lower() + new_path[2:] + return new_path + + +# Looks at the current PATH and adds and removes entries so that the PATH reflects +# the set of given active tools. +def adjusted_path(tools_to_activate, system=False, user=False): + # These directories should be added to PATH + path_add = get_required_path(tools_to_activate) + # These already exist. + if WINDOWS and not MSYS: + existing_path = win_get_environment_variable('PATH', system=system, user=user, fallback=True).split(ENVPATH_SEPARATOR) + else: + existing_path = os.environ['PATH'].split(ENVPATH_SEPARATOR) + emsdk_root_path = to_unix_path(emsdk_path()) + + existing_emsdk_tools = [] + existing_nonemsdk_path = [] + for entry in existing_path: + if to_unix_path(entry).startswith(emsdk_root_path): + existing_emsdk_tools.append(entry) + else: + existing_nonemsdk_path.append(entry) + + new_emsdk_tools = [] + kept_emsdk_tools = [] + for entry in path_add: + if not normalized_contains(existing_emsdk_tools, entry): + new_emsdk_tools.append(entry) + else: + kept_emsdk_tools.append(entry) + + whole_path = unique_items(new_emsdk_tools + kept_emsdk_tools + existing_nonemsdk_path) + + if MSYS: + # XXX Hack: If running native Windows Python in MSYS prompt where PATH + # entries look like "/c/Windows/System32", os.environ['PATH'] + # in Python will transform to show them as "C:\\Windows\\System32", so need + # to reconvert path delimiter back to forward slashes. + whole_path = [to_msys_path(p) for p in whole_path] + new_emsdk_tools = [to_msys_path(p) for p in new_emsdk_tools] + + separator = ':' if MSYS else ENVPATH_SEPARATOR + return (separator.join(whole_path), new_emsdk_tools) + + +def get_env_vars_to_add(tools_to_activate, system, user): + env_vars_to_add = [] + + newpath, added_path = adjusted_path(tools_to_activate, system, user) + + # Don't bother setting the path if there are no changes. + if os.environ['PATH'] != newpath: + env_vars_to_add += [('PATH', newpath)] + + if added_path: + errlog('Adding directories to PATH:') + for item in added_path: + errlog('PATH += ' + item) + errlog('') + + # A core variable EMSDK points to the root of Emscripten SDK directory. + env_vars_to_add += [('EMSDK', to_unix_path(emsdk_path()))] + env_vars_to_add += [('EM_CONFIG', os.path.normpath(dot_emscripten_path()))] + + for tool in tools_to_activate: + config = tool.activated_config() + if 'EMSCRIPTEN_ROOT' in config: + # For older emscripten versions that don't use an embedded cache by + # default we need to export EM_CACHE. + # + # Sadly, we can't put this in the config file since those older versions + # also didn't read the `CACHE` key from the config file: + # + # History: + # - 'CACHE' config started being honored in 1.39.16 + # https://github.com/emscripten-core/emscripten/pull/11091 + # - Default to embedded cache also started in 1.39.16 + # https://github.com/emscripten-core/emscripten/pull/11126 + # + # Since setting EM_CACHE in the environment effects the entire machine + # we want to avoid this except when installing these older emscripten + # versions that really need it. + version = parse_emscripten_version(config['EMSCRIPTEN_ROOT']) + if version < [1, 39, 16]: + em_cache_dir = os.path.join(config['EMSCRIPTEN_ROOT'], 'cache') + env_vars_to_add += [('EM_CACHE', em_cache_dir)] + + envs = tool.activated_environment() + for env in envs: + key, value = parse_key_value(env) + value = to_native_path(tool.expand_vars(value)) + env_vars_to_add += [(key, value)] + + return env_vars_to_add + + +def construct_env(tools_to_activate, system, user): + return construct_env_with_vars(get_env_vars_to_add(tools_to_activate, system, user)) + + +def unset_env(key): + if POWERSHELL: + return 'Remove-Item env:%s\n' % key + if CMD: + return 'set %s=\n' % key + if CSH: + return 'unsetenv %s;\n' % key + if BASH: + return 'unset %s;\n' % key + assert False + + +def construct_env_with_vars(env_vars_to_add): + env_string = '' + if env_vars_to_add: + errlog('Setting environment variables:') + + for key, value in env_vars_to_add: + # Don't set env vars which are already set to the correct value. + if key in os.environ and to_unix_path(os.environ[key]) == to_unix_path(value): + continue + errlog(key + ' = ' + value) + if POWERSHELL: + env_string += '$env:' + key + '="' + value + '"\n' + elif CMD: + env_string += 'SET ' + key + '=' + value + '\n' + elif CSH: + env_string += 'setenv ' + key + ' "' + value + '";\n' + elif BASH: + env_string += 'export ' + key + '="' + value + '";\n' + else: + assert False + + if 'EMSDK_PYTHON' in env_vars_to_add: + # When using our bundled python we never want the user's + # PYTHONHOME or PYTHONPATH + # See https://github.com/emscripten-core/emsdk/issues/598 + env_string += unset_env('PYTHONHOME') + env_string += unset_env('PYTHONPATH') + + # Remove any environment variables that might have been set by old or + # inactive tools/sdks. For example, we set EM_CACHE for older versions + # of the SDK but we want to remove that from the current environment + # if no such tool is active. + # Ignore certain keys that are inputs to emsdk itself. + ignore_keys = set(['EMSDK_POWERSHELL', 'EMSDK_CSH', 'EMSDK_CMD', 'EMSDK_BASH', + 'EMSDK_NUM_CORES', 'EMSDK_TTY']) + env_keys_to_add = set(pair[0] for pair in env_vars_to_add) + for key in os.environ: + if key.startswith('EMSDK_') or key.startswith('EM_'): + if key not in env_keys_to_add and key not in ignore_keys: + errlog('Clearing existing environment variable: %s' % key) + env_string += unset_env(key) + + return env_string + + +def error_on_missing_tool(name): + if name.endswith('-64bit') and not is_os_64bit(): + errlog("Error: '%s' is only provided for 64-bit OSes." % name) + else: + errlog("Error: No tool or SDK found by name '%s'." % name) + return 1 + + +def exit_with_fastcomp_error(): + exit_with_error('The fastcomp backend is not getting new builds or releases. Please use the upstream llvm backend or use an older version than 2.0.0 (such as 1.40.1).') + + +def expand_sdk_name(name, activating): + if 'upstream-master' in name: + errlog('upstream-master SDK has been renamed upstream-main') + name = name.replace('upstream-master', 'upstream-main') + if name in ('latest-fastcomp', 'latest-releases-fastcomp', 'tot-fastcomp', 'sdk-nightly-latest'): + exit_with_fastcomp_error() + if name in ('latest', 'sdk-latest', 'latest-64bit', 'sdk-latest-64bit'): + # This is effectly the default SDK + return str(find_latest_releases_sdk('upstream')) + elif name in ('latest-upstream', 'latest-clang-upstream', 'latest-releases-upstream'): + return str(find_latest_releases_sdk('upstream')) + elif name in ('tot', 'sdk-tot', 'tot-upstream'): + if activating: + # When we are activating a tot release, assume that the currently + # installed SDK, if any, is the tot release we want to activate. + # Without this `install tot && activate tot` will race with the builders + # that are producing new builds. + installed = get_installed_sdk_version() + if installed: + debug_print('activating currently installed SDK; not updating tot version') + return 'sdk-releases-upstream-%s-64bit' % installed + return str(find_tot_sdk()) + else: + # check if it's a release handled by an emscripten-releases version, + # and if so use that by using the right hash. we support a few notations, + # x.y.z[-(upstream|fastcomp_]) + # sdk-x.y.z[-(upstream|fastcomp_])-64bit + # TODO: support short notation for old builds too? + backend = None + fullname = name + if '-upstream' in fullname: + fullname = name.replace('-upstream', '') + backend = 'upstream' + elif '-fastcomp' in fullname: + fullname = fullname.replace('-fastcomp', '') + backend = 'fastcomp' + version = fullname.replace('sdk-', '').replace('releases-', '').replace('-64bit', '').replace('tag-', '') + releases_info = load_releases_info()['releases'] + release_hash = get_release_hash(version, releases_info) + if release_hash: + # Known release hash + if backend == 'fastcomp' and version_key(version) >= (2, 0, 0): + exit_with_fastcomp_error() + if backend is None: + if version_key(version) >= (1, 39, 0): + backend = 'upstream' + else: + backend = 'fastcomp' + return 'sdk-releases-%s-%s-64bit' % (backend, release_hash) + elif len(version) == 40: + if backend is None: + backend = 'upstream' + global extra_release_tag + extra_release_tag = version + return 'sdk-releases-%s-%s-64bit' % (backend, version) + return name + + +def main(args): + + if is_emsdk_sourced_from_github(): + # This code only exists on the master branch + errlog('****') + errlog('Error: You appear to be using the `master` branch of emsdk.') + errlog('We recently made the switch to using `main`') + errlog('In order to continue to receive updates you will need to make the switch locally too.') + errlog('For normal clones without any local branches simply running the following command should be enough:') + errlog(' `git checkout main`') + errlog('For more information see https://github.com/emscripten-core/emsdk/issues/805') + errlog('****') + return 1 + + if not args: + errlog("Missing command; Type 'emsdk help' to get a list of commands.") + return 1 + + cmd = args.pop(0) + + if cmd in ('help', '--help', '-h'): + print(' emsdk: Available commands:') + + print(''' + emsdk list [--old] [--uses] - Lists all available SDKs and tools and their + current installation status. With the --old + parameter, also historical versions are + shown. If --uses is passed, displays the + composition of different SDK packages and + dependencies. + + emsdk update - Updates emsdk to the newest version. If you have + bootstrapped emsdk via cloning directly from + GitHub, call "git pull" instead to update emsdk. + + emsdk install [options] ... + - Downloads and installs given tools or SDKs. + Options can contain: + + -j: Specifies the number of cores to use when + building the tool. Default: use one less + than the # of detected cores. + + --build=: Controls what kind of build of LLVM to + perform. Pass either 'Debug', 'Release', + 'MinSizeRel' or 'RelWithDebInfo'. Default: + 'Release'. + + --generator=: Specifies the CMake Generator to be used + during the build. Possible values are the + same as what your CMake supports and whether + the generator is valid depends on the tools + you have installed. Defaults to 'Unix Makefiles' + on *nix systems. If generator name is multiple + words, enclose with single or double quotes. + + --shallow: When installing tools from one of the git + development branches, this parameter can be + passed to perform a shallow git clone instead + of a full one. This reduces the amount of + network transfer that is needed. This option + should only be used when you are interested in + downloading one of the development branches, + but are not looking to develop Emscripten + yourself. Default: disabled, i.e. do a full + clone. + + --build-tests: If enabled, LLVM is built with internal tests + included. Pass this to enable running test + other.test_llvm_lit in the Emscripten test + suite. Default: disabled. + --enable-assertions: If specified, LLVM is built with assert() + checks enabled. Useful for development + purposes. Default: Enabled + --disable-assertions: Forces assertions off during the build. + + --vs2017/--vs2019: If building from source, overrides to build + using the specified compiler. When installing + precompiled packages, this has no effect. + Note: The same compiler specifier must be + passed to the emsdk activate command to + activate the desired version. + + Notes on building from source: + + To pass custom CMake directives when configuring + LLVM build, specify the environment variable + LLVM_CMAKE_ARGS="param1=value1,param2=value2" + in the environment where the build is invoked. + See README.md for details. + + --override-repository: Specifies the git URL to use for a given Tool. E.g. + --override-repository emscripten-main@https://github.com//emscripten/tree/ + + + emsdk uninstall - Removes the given tool or SDK from disk.''') + + if WINDOWS: + print(''' + emsdk activate [--permanent] [--system] [--build=type] [--vs2017/--vs2019] + + - Activates the given tool or SDK in the + environment of the current shell. + + - If the `--permanent` option is passed, then the environment + variables are set permanently for the current user. + + - If the `--system` option is passed, the registration + is done for all users of the system. + This needs admin privileges + (uses Machine environment variables). + + - If a custom compiler version was used to override + the compiler to use, pass the same --vs2017/--vs2019 parameter + here to choose which version to activate. + + emcmdprompt.bat - Spawns a new command prompt window with the + Emscripten environment active.''') + else: + print(''' emsdk activate [--build=type] + + - Activates the given tool or SDK in the + environment of the current shell.''') + + print(''' + Both commands 'install' and 'activate' accept an optional parameter + '--build=type', which can be used to override what kind of installation + or activation to perform. Possible values for type are Debug, Release, + MinSizeRel or RelWithDebInfo. Note: When overriding a custom build type, + be sure to match the same --build= option to both 'install' and + 'activate' commands and the invocation of 'emsdk_env', or otherwise + these commands will default to operating on the default build type + which in and RelWithDebInfo.''') + return 0 + + # Extracts a boolean command line argument from args and returns True if it was present + def extract_bool_arg(name): + if name in args: + args.remove(name) + return True + return False + + def extract_string_arg(name): + for i in range(len(args)): + if args[i] == name: + value = args[i + 1] + del args[i:i + 2] + return value + + arg_old = extract_bool_arg('--old') + arg_uses = extract_bool_arg('--uses') + arg_permanent = extract_bool_arg('--permanent') + arg_global = extract_bool_arg('--global') + arg_system = extract_bool_arg('--system') + if arg_global: + print('--global is deprecated. Use `--system` to set the environment variables for all users') + arg_system = True + if arg_system: + arg_permanent = True + if extract_bool_arg('--embedded'): + errlog('embedded mode is now the only mode available') + if extract_bool_arg('--no-embedded'): + errlog('embedded mode is now the only mode available') + return 1 + + arg_notty = extract_bool_arg('--notty') + if arg_notty: + global TTY_OUTPUT + TTY_OUTPUT = False + + # Replace meta-packages with the real package names. + if cmd in ('update', 'install', 'activate'): + activating = cmd == 'activate' + args = [expand_sdk_name(a, activating=activating) for a in args] + + load_dot_emscripten() + load_sdk_manifest() + + # Apply any overrides to git branch names to clone from. + forked_url = extract_string_arg('--override-repository') + while forked_url: + tool_name, url_and_refspec = forked_url.split('@') + t = find_tool(tool_name) + if not t: + errlog('Failed to find tool ' + tool_name + '!') + return False + else: + t.url, t.git_branch = parse_github_url_and_refspec(url_and_refspec) + debug_print('Reading git repository URL "' + t.url + '" and git branch "' + t.git_branch + '" for Tool "' + tool_name + '".') + + forked_url = extract_string_arg('--override-repository') + + # Process global args + for i in range(len(args)): + if args[i].startswith('--generator='): + build_generator = re.match(r'''^--generator=['"]?([^'"]+)['"]?$''', args[i]) + if build_generator: + global CMAKE_GENERATOR + CMAKE_GENERATOR = build_generator.group(1) + args[i] = '' + else: + errlog("Cannot parse CMake generator string: " + args[i] + ". Try wrapping generator string with quotes") + return 1 + elif args[i].startswith('--build='): + build_type = re.match(r'^--build=(.+)$', args[i]) + if build_type: + global CMAKE_BUILD_TYPE_OVERRIDE + build_type = build_type.group(1) + build_types = ['Debug', 'MinSizeRel', 'RelWithDebInfo', 'Release'] + try: + build_type_index = [x.lower() for x in build_types].index(build_type.lower()) + CMAKE_BUILD_TYPE_OVERRIDE = build_types[build_type_index] + args[i] = '' + except: + errlog('Unknown CMake build type "' + build_type + '" specified! Please specify one of ' + str(build_types)) + return 1 + else: + errlog("Invalid command line parameter " + args[i] + ' specified!') + return 1 + args = [x for x in args if x] + + if cmd == 'list': + print('') + + def installed_sdk_text(name): + sdk = find_sdk(name) + return 'INSTALLED' if sdk and sdk.is_installed() else '' + + if (LINUX or MACOS or WINDOWS) and (ARCH == 'x86' or ARCH == 'x86_64'): + print('The *recommended* precompiled SDK download is %s (%s).' % (find_latest_releases_version(), find_latest_releases_hash())) + print() + print('To install/activate it, use one of:') + print(' latest [default (llvm) backend]') + print(' latest-fastcomp [legacy (fastcomp) backend]') + print('') + print('Those are equivalent to installing/activating the following:') + print(' %s %s' % (find_latest_releases_version(), installed_sdk_text(find_latest_releases_sdk('upstream')))) + print(' %s-fastcomp %s' % (find_latest_releases_version(), installed_sdk_text(find_latest_releases_sdk('fastcomp')))) + print('') + else: + print('Warning: your platform does not have precompiled SDKs available.') + print('You may install components from source.') + print('') + + print('All recent (non-legacy) installable versions are:') + releases_versions = sorted( + load_releases_versions(), + key=lambda x: [int(v) if v.isdigit() else -1 for v in x.split('.')], + reverse=True, + ) + releases_info = load_releases_info()['releases'] + for ver in releases_versions: + print(' %s %s' % (ver, installed_sdk_text('sdk-releases-upstream-%s-64bit' % get_release_hash(ver, releases_info)))) + print() + + # Use array to work around the lack of being able to mutate from enclosing + # function. + has_partially_active_tools = [False] + + if sdks: + def find_sdks(needs_compilation): + s = [] + for sdk in sdks: + if sdk.is_old and not arg_old: + continue + if sdk.needs_compilation() == needs_compilation: + s += [sdk] + return s + + def print_sdks(s): + for sdk in s: + installed = '\tINSTALLED' if sdk.is_installed() else '' + active = '*' if sdk.is_active() else ' ' + print(' ' + active + ' {0: <25}'.format(str(sdk)) + installed) + if arg_uses: + for dep in sdk.uses: + print(' - {0: <25}'.format(dep)) + print('') + print('The additional following precompiled SDKs are also available for download:') + print_sdks(find_sdks(False)) + + print('The following SDKs can be compiled from source:') + print_sdks(find_sdks(True)) + + if tools: + def find_tools(needs_compilation): + t = [] + for tool in tools: + if tool.is_old and not arg_old: + continue + if tool.needs_compilation() != needs_compilation: + continue + t += [tool] + return t + + def print_tools(t): + for tool in t: + if tool.is_old and not arg_old: + continue + if tool.can_be_installed() is True: + installed = '\tINSTALLED' if tool.is_installed() else '' + else: + installed = '\tNot available: ' + tool.can_be_installed() + tool_is_active = tool.is_active() + tool_is_env_active = tool_is_active and tool.is_env_active() + if tool_is_env_active: + active = ' * ' + elif tool_is_active: + active = '(*)' + has_partially_active_tools[0] = has_partially_active_tools[0] or True + else: + active = ' ' + print(' ' + active + ' {0: <25}'.format(str(tool)) + installed) + print('') + + print('The following precompiled tool packages are available for download:') + print_tools(find_tools(needs_compilation=False)) + print('The following tools can be compiled from source:') + print_tools(find_tools(needs_compilation=True)) + else: + if is_emsdk_sourced_from_github(): + print("There are no tools available. Run 'git pull' to fetch the latest set of tools.") + else: + print("There are no tools available. Run 'emsdk update' to fetch the latest set of tools.") + print('') + + print('Items marked with * are activated for the current user.') + if has_partially_active_tools[0]: + env_cmd = 'emsdk_env.bat' if WINDOWS else 'source ./emsdk_env.sh' + print('Items marked with (*) are selected for use, but your current shell environment is not configured to use them. Type "' + env_cmd + '" to set up your current shell to use them' + (', or call "emsdk activate --permanent " to permanently activate them.' if WINDOWS else '.')) + if not arg_old: + print('') + print("To access the historical archived versions, type 'emsdk list --old'") + + print('') + if is_emsdk_sourced_from_github(): + print('Run "git pull" to pull in the latest list.') + else: + print('Run "./emsdk update" to pull in the latest list.') + + return 0 + elif cmd == 'construct_env': + # Clean up old temp file up front, in case of failure later before we get + # to write out the new one. + tools_to_activate = currently_active_tools() + tools_to_activate = process_tool_list(tools_to_activate, log_errors=True) + env_string = construct_env(tools_to_activate, arg_system, arg_permanent) + if WINDOWS and not BASH: + write_set_env_script(env_string) + else: + sys.stdout.write(env_string) + return 0 + elif cmd == 'update': + update_emsdk() + if WINDOWS: + # Clean up litter after old emsdk update which may have left this temp + # file around. + rmfile(sdk_path(EMSDK_SET_ENV)) + return 0 + elif cmd == 'update-tags': + errlog('`update-tags` is not longer needed. To install the latest tot release just run `install tot`') + return 0 + elif cmd == 'activate': + if arg_permanent: + print('Registering active Emscripten environment permanently') + print('') + + tools_to_activate = currently_active_tools() + args = [x for x in args if not x.startswith('--')] + for arg in args: + tool = find_tool(arg) + if tool is None: + tool = find_sdk(arg) + if tool is None: + return error_on_missing_tool(arg) + tools_to_activate += [tool] + if not tools_to_activate: + errlog('No tools/SDKs specified to activate! Usage:\n emsdk activate tool/sdk1 [tool/sdk2] [...]') + return 1 + active_tools = set_active_tools(tools_to_activate, permanently_activate=arg_permanent, system=arg_system) + if not active_tools: + errlog('No tools/SDKs found to activate! Usage:\n emsdk activate tool/sdk1 [tool/sdk2] [...]') + return 1 + if WINDOWS and not arg_permanent: + errlog('The changes made to environment variables only apply to the currently running shell instance. Use the \'emsdk_env.bat\' to re-enter this environment later, or if you\'d like to permanently register this environment permanently, rerun this command with the option --permanent.') + return 0 + elif cmd == 'install': + global BUILD_FOR_TESTING, ENABLE_LLVM_ASSERTIONS, CPU_CORES, GIT_CLONE_SHALLOW + + # Process args + for i in range(len(args)): + if args[i].startswith('-j'): + multicore = re.match(r'^-j(\d+)$', args[i]) + if multicore: + CPU_CORES = int(multicore.group(1)) + args[i] = '' + else: + errlog("Invalid command line parameter " + args[i] + ' specified!') + return 1 + elif args[i] == '--shallow': + GIT_CLONE_SHALLOW = True + args[i] = '' + elif args[i] == '--build-tests': + BUILD_FOR_TESTING = True + args[i] = '' + elif args[i] == '--enable-assertions': + ENABLE_LLVM_ASSERTIONS = 'ON' + args[i] = '' + elif args[i] == '--disable-assertions': + ENABLE_LLVM_ASSERTIONS = 'OFF' + args[i] = '' + args = [x for x in args if x] + if not args: + errlog("Missing parameter. Type 'emsdk install ' to install a tool or an SDK. Type 'emsdk list' to obtain a list of available tools. Type 'emsdk install latest' to automatically install the newest version of the SDK.") + return 1 + + for t in args: + tool = find_tool(t) + if tool is None: + tool = find_sdk(t) + if tool is None: + return error_on_missing_tool(t) + tool.install() + return 0 + elif cmd == 'uninstall': + if not args: + errlog("Syntax error. Call 'emsdk uninstall '. Call 'emsdk list' to obtain a list of available tools.") + return 1 + tool = find_tool(args[0]) + if tool is None: + errlog("Error: Tool by name '" + args[0] + "' was not found.") + return 1 + tool.uninstall() + return 0 + + errlog("Unknown command '" + cmd + "' given! Type 'emsdk help' to get a list of commands.") + return 1 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/emsdk_env.bat b/emsdk_env.bat index 1f8d1650bb..c793321c98 100644 --- a/emsdk_env.bat +++ b/emsdk_env.bat @@ -1 +1 @@ -@call "%~dp0emsdk" construct_env %* +@call "%~dp0emsdk" construct_env diff --git a/emsdk_env.csh b/emsdk_env.csh new file mode 100644 index 0000000000..187d4c5438 --- /dev/null +++ b/emsdk_env.csh @@ -0,0 +1,30 @@ +# This script is sourced by the user and uses +# their shell. Try not to use tcshisms. + +# Do not execute this script without sourcing, +# because it won't have any effect then. +# That is, always run this script with +# +# source ./emsdk_env.csh +# +# instead of just plainly running with +# +# ./emsdk_env.csh +# +# which won't have any effect. +set SRC=($_) +if ("$SRC" == "") then + set SRC="$0" +else + set SRC="$SRC[1]" +endif +set CURDIR=`pwd` +setenv DIR `dirname "$SRC"` +unset SRC + +setenv EMSDK_CSH 1 + +eval `$DIR/emsdk construct_env` +unsetenv DIR + +unsetenv EMSDK_CSH diff --git a/emsdk_env.fish b/emsdk_env.fish old mode 100755 new mode 100644 index 9f6e15a79d..29f6f4647b --- a/emsdk_env.fish +++ b/emsdk_env.fish @@ -6,12 +6,7 @@ set -l script (status -f) set -l dir (dirname $script) -pushd $dir > /dev/null - -./emsdk construct_env "$argv" -. ./emsdk_set_env.sh +eval ($dir/emsdk construct_env) set -e -l script set -e -l dir - -popd > /dev/null diff --git a/emsdk_env.ps1 b/emsdk_env.ps1 index fc7f2c7692..ab5fc4dc45 100644 --- a/emsdk_env.ps1 +++ b/emsdk_env.ps1 @@ -1,2 +1,2 @@ $ScriptDirectory = Split-Path -parent $PSCommandPath -& "$ScriptDirectory/emsdk.ps1" construct_env $args +& "$ScriptDirectory/emsdk.ps1" construct_env diff --git a/emsdk_env.sh b/emsdk_env.sh old mode 100755 new mode 100644 index df11073891..8f76de7522 --- a/emsdk_env.sh +++ b/emsdk_env.sh @@ -1,13 +1,20 @@ # This script is sourced by the user and uses -# their shell. Try not to use bashisms. - +# their shell. +# +# This script tries to find its location but +# this does not work in every shell. +# +# It is known to work in bash, zsh and ksh +# # Do not execute this script without sourcing, # because it won't have any effect then. # That is, always run this script with # -# . ./emsdk_env.sh +# . /path/to/emsdk_env.sh +# # or -# source ./emsdk_env.sh +# +# source /path/to/emsdk_env.sh # # instead of just plainly running with # @@ -15,15 +22,52 @@ # # which won't have any effect. -SRC="$BASH_SOURCE" -if [ "$SRC" = "" ]; then - SRC="$0" +CURRENT_SCRIPT= +DIR="." + +# use shell specific method to get the path +# to the current file being source'd. +# +# To add a shell, add another conditional below, +# then add tests to scripts/test_source_env.sh + +if [ -n "${BASH_SOURCE-}" ]; then + CURRENT_SCRIPT="$BASH_SOURCE" +elif [ -n "${ZSH_VERSION-}" ]; then + CURRENT_SCRIPT="${(%):-%x}" +elif [ -n "${KSH_VERSION-}" ]; then + CURRENT_SCRIPT=${.sh.file} fi -CURDIR="$(pwd)" -cd "$(dirname "$SRC")" -unset SRC -./emsdk construct_env "$@" -. ./emsdk_set_env.sh +if [ -n "${CURRENT_SCRIPT-}" ]; then + DIR=$(dirname "$CURRENT_SCRIPT") + if [ -h "$CURRENT_SCRIPT" ]; then + # Now work out actual DIR since this is part of a symlink. + # Since we can't be sure that readlink or realpath + # are available, use tools more likely to be installed. + # (This will still fail if sed is not available.) + SYMDIR=$(dirname "$(ls -l "$CURRENT_SCRIPT" | sed -n "s/.*-> //p")") + if [ -z "$SYMDIR" ]; then + SYMDIR="." + fi + FULLDIR="$DIR/$SYMDIR" + DIR=$(cd "$FULLDIR" > /dev/null 2>&1; /bin/pwd) + unset SYMDIR + unset FULLDIR + fi +fi +unset CURRENT_SCRIPT + +if [ ! -f "$DIR/emsdk.py" ]; then + echo "Error: unable to determine 'emsdk' directory. Perhaps you are using a shell or" 1>&2 + echo " environment that this script does not support." 1>&2 + echo 1>&2 + echo "A possible solution is to source this script while in the 'emsdk' directory." 1>&2 + echo 1>&2 + unset DIR + return +fi -cd "$CURDIR" +# Force emsdk to use bash syntax so that this works in windows + bash too +eval `EMSDK_BASH=1 $DIR/emsdk construct_env` +unset DIR diff --git a/emsdk_manifest.json b/emsdk_manifest.json index c7a48da4cc..21b4508c2c 100644 --- a/emsdk_manifest.json +++ b/emsdk_manifest.json @@ -1,1772 +1,686 @@ -{ - "tools": [ - { - "id": "clang", - "version": "3.2", - "bitness": 32, - "windows_url": "clang_3.2_32bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "3.2", - "bitness": 64, - "windows_url": "clang_3.2_64bit.zip", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "LLVM_ROOT='%installation_dir%/bin'", - "activated_env": "LLVM_ROOT=%installation_dir%/bin", - "is_old": true - }, - { - "id": "clang", - "version": "3.3", - "bitness": 32, - "windows_url": "clang_3.3_32bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "3.3", - "bitness": 64, - "windows_url": "clang_3.3_64bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "3.2", - "bitness": 64, - "osx_url": "clang%2Bllvm-3.2-x86_64-apple-darwin11.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "LLVM_ROOT='%installation_dir%/bin'", - "activated_env": "LLVM_ROOT=%installation_dir%/bin", - "is_old": true - }, - { - "id": "clang", - "version": "3.3", - "bitness": 64, - "osx_url": "clang%2Bllvm-3.3-x86_64-apple-darwin12.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "LLVM_ROOT='%installation_dir%/bin'", - "activated_env": "LLVM_ROOT=%installation_dir%/bin", - "is_old": true - }, - { - "id": "clang", - "version": "tag-e%tag%", - "bitness": 32, - "append_bitness": false, - "windows_url": "https://github.com/kripken/emscripten-fastcomp/archive/%tag%.zip", - "unix_url": "https://github.com/kripken/emscripten-fastcomp/archive/%tag%.tar.gz", - "windows_clang_url": "https://github.com/kripken/emscripten-fastcomp-clang/archive/%tag%.zip", - "unix_clang_url": "https://github.com/kripken/emscripten-fastcomp-clang/archive/%tag%.tar.gz", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "clang", - "version": "tag-e%tag%", - "bitness": 64, - "append_bitness": false, - "windows_url": "https://github.com/kripken/emscripten-fastcomp/archive/%tag%.zip", - "unix_url": "https://github.com/kripken/emscripten-fastcomp/archive/%tag%.tar.gz", - "windows_clang_url": "https://github.com/kripken/emscripten-fastcomp-clang/archive/%tag%.zip", - "unix_clang_url": "https://github.com/kripken/emscripten-fastcomp-clang/archive/%tag%.tar.gz", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "clang", - "version": "nightly-e%nightly-llvm-64bit%", - "bitness": 64, - "append_bitness": false, - "windows_url": "llvm/nightly/win_64bit/emscripten-llvm-e%nightly-llvm-64bit%.zip", - "osx_url": "llvm/nightly/osx_64bit/emscripten-llvm-e%nightly-llvm-64bit%.tar.gz", - "linux_url": "llvm/nightly/linux_64bit/emscripten-llvm-e%nightly-llvm-64bit%.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%" - }, - { - "id": "clang", - "version": "incoming", - "bitness": 32, - "install_path": "clang/fastcomp", - "url": "https://github.com/kripken/emscripten-fastcomp.git", - "clang_url": "https://github.com/kripken/emscripten-fastcomp-clang.git", - "git_branch": "incoming", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "clang", - "version": "incoming", - "bitness": 64, - "install_path": "clang/fastcomp", - "git_branch": "incoming", - "url": "https://github.com/kripken/emscripten-fastcomp.git", - "clang_url": "https://github.com/kripken/emscripten-fastcomp-clang.git", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "clang", - "version": "master", - "bitness": 32, - "install_path": "clang/fastcomp", - "url": "https://github.com/kripken/emscripten-fastcomp.git", - "clang_url": "https://github.com/kripken/emscripten-fastcomp-clang.git", - "git_branch": "master", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "clang", - "version": "master", - "bitness": 64, - "install_path": "clang/fastcomp", - "git_branch": "master", - "url": "https://github.com/kripken/emscripten-fastcomp.git", - "clang_url": "https://github.com/kripken/emscripten-fastcomp-clang.git", - "custom_install_script": "build_fastcomp", - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "upstream-clang", - "version": "master", - "bitness": 32, - "install_path": "clang/upstream", - "git_branch": "master", - "url": "https://github.com/llvm-mirror/llvm.git", - "clang_url": "https://github.com/llvm-mirror/clang.git", - "lld_url": "https://github.com/llvm-mirror/lld.git", - "custom_install_script": "build_fastcomp", - "only_supports_wasm": true, - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "upstream-clang", - "version": "master", - "bitness": 64, - "install_path": "clang/upstream", - "git_branch": "master", - "url": "https://github.com/llvm-mirror/llvm.git", - "clang_url": "https://github.com/llvm-mirror/clang.git", - "lld_url": "https://github.com/llvm-mirror/lld.git", - "custom_install_script": "build_fastcomp", - "only_supports_wasm": true, - "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%", - "cmake_build_type": "Release" - }, - { - "id": "waterfall", - "version": "upstream-%waterfall-lkgr%", - "bitness": 64, - "linux_url": "https://storage.googleapis.com/wasm-llvm/builds/linux/%waterfall-lkgr%/wasm-binaries.tbz2", - "zipfile_prefix": "%waterfall-lkgr%-", - "install_path": "upstream/%waterfall-lkgr%", - "activated_path": "%installation_dir%/emscripten", - "activated_cfg": "LLVM_ROOT='%installation_dir%/bin';BINARYEN_ROOT='%installation_dir%'" - }, - { - "id": "waterfall", - "version": "fastcomp-%waterfall-lkgr%", - "bitness": 64, - "linux_url": "https://storage.googleapis.com/wasm-llvm/builds/linux/%waterfall-lkgr%/wasm-binaries.tbz2", - "zipfile_prefix": "%waterfall-lkgr%-", - "install_path": "fastcomp/%waterfall-lkgr%", - "activated_path": "%installation_dir%/emscripten", - "activated_cfg": "LLVM_ROOT='%installation_dir%/fastcomp/bin';BINARYEN_ROOT='%installation_dir%'" - }, - - { - "id": "releases", - "version": "upstream-%releases-tag%", - "bitness": 64, - "linux_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/%releases-tag%/wasm-binaries.tbz2", - "osx_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/mac/%releases-tag%/wasm-binaries.tbz2", - "windows_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/%releases-tag%/wasm-binaries.zip", - "zipfile_prefix": "%releases-tag%-", - "install_path": "upstream/%releases-tag%", - "activated_path": "%installation_dir%/emscripten", - "activated_cfg": "LLVM_ROOT='%installation_dir%/bin';BINARYEN_ROOT='%installation_dir%'", - "emscripten_releases_hash": "%releases-tag%" - }, - { - "id": "releases", - "version": "fastcomp-%releases-tag%", - "bitness": 64, - "linux_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/%releases-tag%/wasm-binaries.tbz2", - "osx_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/mac/%releases-tag%/wasm-binaries.tbz2", - "windows_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/%releases-tag%/wasm-binaries.zip", - "zipfile_prefix": "%releases-tag%-", - "install_path": "fastcomp/%releases-tag%", - "activated_path": "%installation_dir%/emscripten", - "activated_cfg": "LLVM_ROOT='%installation_dir%/fastcomp/bin';BINARYEN_ROOT='%installation_dir%'", - "emscripten_releases_hash": "%releases-tag%" - }, - - { - "id": "clang", - "version": "e1.13.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.13.0.zip", - "osx_url": "emscripten-clang_e1.13.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.13.1", - "bitness": 64, - "windows_url": "emscripten-clang_e1.13.1.zip", - "osx_url": "emscripten-clang_e1.13.1.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.16.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.16.0.zip", - "osx_url": "emscripten-clang_e1.16.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.21.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.21.0.zip", - "osx_url": "emscripten-clang_e1.21.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.22.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.22.0.zip", - "osx_url": "emscripten-clang_e1.22.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.25.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.25.0.zip", - "osx_url": "emscripten-clang_e1.25.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.27.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.27.0.zip", - "osx_url": "emscripten-clang_e1.27.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%'", - "activated_env": "LLVM_ROOT=%installation_dir%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.29.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.29.0.zip", - "osx_url": "emscripten-clang_e1.29.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%", - "is_old": true - }, - { - "id": "clang", - "version": "e1.30.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.30.0.zip", - "osx_url": "emscripten-clang_e1.30.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%" - }, - { - "id": "clang", - "version": "e1.34.1", - "bitness": 64, - "windows_url": "emscripten-clang_e1.34.1.zip", - "osx_url": "emscripten-clang_e1.34.1.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%" - }, - { - "id": "clang", - "version": "e1.35.0", - "bitness": 64, - "windows_url": "emscripten-clang_e1.35.0.zip", - "osx_url": "emscripten-clang_e1.35.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%" - }, - { - "id": "clang", - "version": "e1.37.1", - "bitness": 64, - "windows_url": "llvm/tag/win_64bit/emscripten-llvm-e1.37.1.zip", - "osx_url": "llvm/tag/osx_64bit/emscripten-llvm-e1.37.1.tar.gz", - "linux_url": "llvm/tag/linux_64bit/emscripten-llvm-e1.37.1.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%';BINARYEN_ROOT='%installation_dir%/binaryen'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%;BINARYEN_ROOT=%installation_dir%/binaryen" - }, - { - "id": "clang", - "version": "e%precompiled_tag32%", - "bitness": 32, - "windows_url": "llvm/tag/win_64bit/emscripten-llvm-e%precompiled_tag32%.zip", - "osx_url": "llvm/tag/osx_64bit/emscripten-llvm-e%precompiled_tag32%.tar.gz", - "linux_url": "llvm/tag/linux_64bit/emscripten-llvm-e%precompiled_tag32%.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%';BINARYEN_ROOT='%installation_dir%/binaryen'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%;BINARYEN_ROOT=%installation_dir%/binaryen" - }, - { - "id": "clang", - "version": "e%precompiled_tag64%", - "bitness": 64, - "windows_url": "llvm/tag/win_64bit/emscripten-llvm-e%precompiled_tag64%.zip", - "osx_url": "llvm/tag/osx_64bit/emscripten-llvm-e%precompiled_tag64%.tar.gz", - "linux_url": "llvm/tag/linux_64bit/emscripten-llvm-e%precompiled_tag64%.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%';BINARYEN_ROOT='%installation_dir%/binaryen'", - "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%;BINARYEN_ROOT=%installation_dir%/binaryen" - }, - { - "id": "node", - "version": "0.10.17", - "bitness": 32, - "windows_url": "node_0.10.17_32bit.exe", - "windows_install_path": "node/0.10.17_32bit/node.exe", - "activated_path": "%installation_dir%", - "activated_cfg": "NODE_JS='%installation_dir%/node.exe'", - "activated_env": "EMSDK_NODE=%installation_dir%/node.exe", - "is_old": true - }, - { - "id": "node", - "version": "0.10.17", - "bitness": 64, - "windows_url": "node_0.10.17_64bit.exe", - "windows_install_path": "node/0.10.17_64bit/node.exe", - "activated_path": "%installation_dir%", - "activated_cfg": "NODE_JS='%installation_dir%/node.exe'", - "activated_env": "EMSDK_NODE=%installation_dir%/node.exe", - "is_old": true - }, - { - "id": "node", - "version": "0.10.18", - "bitness": 64, - "osx_url": "node-v0.10.18-darwin-x64.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node", - "is_old": true - }, - { - "id": "node", - "version": "0.12.2", - "bitness": 32, - "windows_url": "node_0.12.2_32bit.exe", - "windows_install_path": "node/0.12.2_32bit/node.exe", - "activated_path": "%installation_dir%", - "activated_cfg": "NODE_JS='%installation_dir%/node.exe'", - "activated_env": "EMSDK_NODE=%installation_dir%/node.exe", - "is_old": true - }, - { - "id": "node", - "version": "0.12.2", - "bitness": 64, - "windows_url": "node_0.12.2_64bit.exe", - "windows_install_path": "node/0.12.2_64bit/node.exe", - "activated_path": "%installation_dir%", - "activated_cfg": "NODE_JS='%installation_dir%/node.exe'", - "activated_env": "EMSDK_NODE=%installation_dir%/node.exe", - "is_old": true - }, - { - "id": "node", - "version": "0.12.2", - "bitness": 64, - "osx_url": "node-v0.12.2-darwin-x64.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node", - "is_old": true - }, - { - "id": "node", - "version": "4.1.1", - "bitness": 32, - "windows_url": "node_4.1.1_32bit.zip", - "linux_url": "node-v4.1.1-linux-x86.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" - }, - { - "id": "node", - "version": "4.1.1", - "bitness": 64, - "osx_url": "node-v4.1.1-darwin-x64.tar.gz", - "windows_url": "node_4.1.1_64bit.zip", - "linux_url": "node-v4.1.1-linux-x64.tar.gz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" - }, - { - "id": "node", - "version": "8.9.1", - "bitness": 32, - "windows_url": "node-v8.9.1-win-x86.zip", - "linux_url": "node-v8.9.1-linux-x86.tar.xz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" - }, - { - "id": "node", - "version": "8.9.1", - "bitness": 64, - "osx_url": "node-v8.9.1-darwin-x64.tar.gz", - "windows_url": "node-v8.9.1-win-x64.zip", - "linux_url": "node-v8.9.1-linux-x64.tar.xz", - "activated_path": "%installation_dir%/bin", - "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", - "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" - }, - { - "id": "python", - "version": "2.7.5.1", - "bitness": 32, - "windows_url": "python_2.7.5.1_32bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "PYTHON='%installation_dir%/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python%.exe%", - "is_old": true - }, - { - "id": "python", - "version": "2.7.5", - "bitness": 64, - "windows_url": "python_2.7.5_64bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "PYTHON='%installation_dir%/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python%.exe%", - "is_old": true - }, - { - "id": "python", - "version": "2.7.5.3", - "bitness": 32, - "windows_url": "python_2.7.5.3_32bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "PYTHON='%installation_dir%/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python%.exe%", - "is_old": true - }, - { - "id": "python", - "version": "2.7.5.3", - "bitness": 64, - "windows_url": "python_2.7.5.3_64bit.zip", - "activated_path": "%installation_dir%", - "activated_cfg": "PYTHON='%installation_dir%/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python%.exe%", - "is_old": true - }, - { - "id": "python", - "version": "2.7.13.1", - "bitness": 32, - "windows_url": "WinPython-32bit-2.7.13.1Zero.zip", - "activated_path": "%installation_dir%/python-2.7.13", - "activated_cfg": "PYTHON='%installation_dir%/python-2.7.13/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python-2.7.13/python%.exe%" - }, - { - "id": "python", - "version": "2.7.13.1", - "bitness": 64, - "windows_url": "WinPython-64bit-2.7.13.1Zero.zip", - "activated_path": "%installation_dir%/python-2.7.13.amd64", - "activated_cfg": "PYTHON='%installation_dir%/python-2.7.13.amd64/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python-2.7.13.amd64/python%.exe%" - }, - { - "id": "python", - "version": "3.5.4", - "bitness": 32, - "windows_url": "WinPython-32bit-3.5.4.1Zero.zip", - "activated_path": "%installation_dir%/python-3.5.4", - "activated_cfg": "PYTHON='%installation_dir%/python-3.5.4/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python-3.5.4/python%.exe%" - }, - { - "id": "python", - "version": "3.5.4", - "bitness": 64, - "windows_url": "WinPython-64bit-3.5.4.1Zero.zip", - "activated_path": "%installation_dir%/python-3.5.4.amd64", - "activated_cfg": "PYTHON='%installation_dir%/python-3.5.4.amd64/python%.exe%'", - "activated_env": "EMSDK_PYTHON=%installation_dir%/python-3.5.4.amd64/python%.exe%" - }, - { - "id": "java", - "version": "7.45", - "bitness": 32, - "windows_url": "portable_jre_7_update_45_32bit.zip", - "activated_path": "%installation_dir%/bin", - "activated_env": "JAVA_HOME=%installation_dir%", - "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" - }, - { - "id": "java", - "version": "7.45", - "bitness": 64, - "windows_url": "portable_jre_7_update_45_64bit.zip", - "activated_path": "%installation_dir%/bin", - "activated_env": "JAVA_HOME=%installation_dir%", - "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" - }, - { - "id": "java", - "version": "8.152", - "bitness": 32, - "windows_url": "portable_jre_8_update_152_32bit.zip", - "activated_path": "%installation_dir%/bin", - "activated_env": "JAVA_HOME=%installation_dir%", - "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" - }, - { - "id": "java", - "version": "8.152", - "bitness": 64, - "windows_url": "portable_jre_8_update_152_64bit.zip", - "activated_path": "%installation_dir%/bin", - "activated_env": "JAVA_HOME=%installation_dir%", - "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" - }, - { - "id": "spidermonkey", - "version": "27.0.1", - "bitness": 64, - "windows_url": "spidermonkey-27.0.1.zip", - "osx_url": "spidermonkey-27.0.1.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "SPIDERMONKEY_ENGINE='%installation_dir%/js%.exe%'", - "is_old": true - }, - { - "id": "spidermonkey", - "version": "nightly-2014-03-13", - "bitness": 64, - "windows_url": "spidermonkey-nightly-2014-03-13.zip", - "osx_url": "spidermonkey-nightly-2014-03-13.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "SPIDERMONKEY_ENGINE='%installation_dir%/js%.exe%'", - "is_old": true - }, - { - "id": "spidermonkey", - "version": "30.0.0", - "bitness": 64, - "windows_url": "spidermonkey-30.0.0.zip", - "osx_url": "spidermonkey-30.0.0.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "SPIDERMONKEY_ENGINE='%installation_dir%/js%.exe%'", - "is_old": true - }, - { - "id": "spidermonkey", - "version": "37.0.1", - "bitness": 64, - "windows_url": "spidermonkey-37.0.1_64bit.zip", - "osx_url": "spidermonkey-37.0.1_64bit.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "SPIDERMONKEY_ENGINE='%installation_dir%/js%.exe%'" - }, - { - "id": "spidermonkey", - "version": "nightly-2015-04-12", - "bitness": 64, - "windows_url": "spidermonkey-nightly-2015-04-12_64bit.zip", - "osx_url": "spidermonkey-nightly-2015-04-12_64bit.tar.gz", - "activated_path": "%installation_dir%", - "activated_cfg": "SPIDERMONKEY_ENGINE='%installation_dir%/js%.exe%'" - }, - { - "id": "git", - "version": "1.8.3", - "windows_url": "git_1.8.3.zip", - "activated_path": "%installation_dir%/bin;%installation_dir%/cmd", - "activated_cfg": "EMSDK_GIT='%installation_dir%'", - "is_old": true - }, - { - "id": "git", - "version": "1.9.4", - "windows_url": "git_1.9.4.zip", - "activated_path": "%installation_dir%/bin;%installation_dir%/cmd", - "activated_cfg": "EMSDK_GIT='%installation_dir%'" - }, - { - "id": "emscripten", - "version": "1.5.6", - "windows_url": "emscripten_1.5.6.zip", - "unix_url": "emscripten_1.5.6.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.7.1", - "windows_url": "emscripten-1.7.1.zip", - "unix_url": "emscripten-1.7.1.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.7.8", - "windows_url": "emscripten-1.7.8.zip", - "unix_url": "emscripten-1.7.8.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.8.2", - "windows_url": "emscripten-1.8.2.zip", - "unix_url": "emscripten-1.8.2.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.9.5", - "windows_url": "emscripten-1.9.5.zip", - "unix_url": "emscripten-1.9.5.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.10.4", - "windows_url": "emscripten-1.10.4.zip", - "unix_url": "emscripten-1.10.4.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.12.0", - "windows_url": "emscripten-1.12.0.zip", - "unix_url": "emscripten-1.12.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.13.0", - "windows_url": "emscripten-1.13.0.zip", - "unix_url": "emscripten-1.13.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.13.1", - "windows_url": "emscripten-1.13.1.zip", - "unix_url": "emscripten-1.13.1.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.16.0", - "windows_url": "emscripten-1.16.0.zip", - "unix_url": "emscripten-1.16.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.21.0", - "windows_url": "emscripten-1.21.0.zip", - "unix_url": "emscripten-1.21.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.22.0", - "windows_url": "emscripten-1.22.0.zip", - "unix_url": "emscripten-1.22.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.25.0", - "windows_url": "emscripten-1.25.0.zip", - "unix_url": "emscripten-1.25.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.27.0", - "windows_url": "emscripten-1.27.0.zip", - "unix_url": "emscripten-1.27.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.29.0", - "windows_url": "emscripten-1.29.0.zip", - "unix_url": "emscripten-1.29.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%", - "is_old": true - }, - { - "id": "emscripten", - "version": "1.30.0", - "windows_url": "emscripten-1.30.0.zip", - "unix_url": "emscripten-1.30.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "emscripten", - "version": "1.34.1", - "windows_url": "emscripten-1.34.1.zip", - "unix_url": "emscripten-1.34.1.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "emscripten", - "version": "1.35.0", - "windows_url": "emscripten-1.35.0.zip", - "unix_url": "emscripten-1.35.0.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "emscripten", - "version": "1.37.1", - "windows_url": "https://github.com/kripken/emscripten/archive/1.37.1.zip", - "unix_url": "https://github.com/kripken/emscripten/archive/1.37.1.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "emscripten", - "version": "tag-%tag%", - "bitness": 32, - "append_bitness": false, - "windows_url": "https://github.com/kripken/emscripten/archive/%tag%.zip", - "unix_url": "https://github.com/kripken/emscripten/archive/%tag%.tar.gz", - "zipfile_prefix": "emscripten-e", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "emscripten", - "version": "tag-%tag%", - "bitness": 64, - "append_bitness": false, - "windows_url": "https://github.com/kripken/emscripten/archive/%tag%.zip", - "unix_url": "https://github.com/kripken/emscripten/archive/%tag%.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "emscripten", - "version": "%precompiled_tag%", - "windows_url": "https://github.com/kripken/emscripten/archive/%precompiled_tag%.zip", - "unix_url": "https://github.com/kripken/emscripten/archive/%precompiled_tag%.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "binaryen", - "version": "tag-%binaryen_tag%", - "bitness": 32, - "append_bitness": false, - "windows_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.zip", - "unix_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.tar.gz", - "zipfile_prefix": "binaryen-e", - "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", - "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", - "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", - "cmake_build_type": "Release", - "custom_install_script": "build_binaryen", - "custom_is_installed_script": "is_binaryen_installed", - "custom_uninstall_script": "uninstall_binaryen" - }, - { - "id": "binaryen", - "version": "tag-%binaryen_tag%", - "bitness": 64, - "append_bitness": false, - "windows_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.zip", - "unix_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.tar.gz", - "zipfile_prefix": "binaryen-e", - "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", - "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", - "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", - "cmake_build_type": "Release", - "custom_install_script": "build_binaryen", - "custom_is_installed_script": "is_binaryen_installed", - "custom_uninstall_script": "uninstall_binaryen" - }, - { - "id": "emscripten", - "version": "nightly-%nightly-emscripten%", - "windows_url": "emscripten/nightly/win/emscripten-nightly-%nightly-emscripten%.zip", - "unix_url": "emscripten/nightly/linux/emscripten-nightly-%nightly-emscripten%.tar.gz", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%" - }, - { - "id": "emscripten", - "version": "incoming", - "bitness": 32, - "append_bitness": false, - "url": "https://github.com/kripken/emscripten.git", - "git_branch": "incoming", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "emscripten", - "version": "master", - "bitness": 32, - "append_bitness": false, - "url": "https://github.com/kripken/emscripten.git", - "git_branch": "master", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "emscripten", - "version": "incoming", - "bitness": 64, - "append_bitness": false, - "url": "https://github.com/kripken/emscripten.git", - "git_branch": "incoming", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "emscripten", - "version": "master", - "bitness": 64, - "append_bitness": false, - "url": "https://github.com/kripken/emscripten.git", - "git_branch": "master", - "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", - "activated_path": "%installation_dir%", - "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", - "cmake_build_type": "Release", - "custom_install_script": "build_optimizer", - "custom_is_installed_script": "is_optimizer_installed", - "custom_uninstall_script": "uninstall_optimizer" - }, - { - "id": "binaryen", - "version": "master", - "bitness": 32, - "append_bitness": false, - "url": "https://github.com/WebAssembly/binaryen.git", - "git_branch": "master", - "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_32bit_binaryen'", - "activated_path": "%installation_dir%%generator_prefix%_32bit_binaryen/bin", - "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_32bit_binaryen", - "cmake_build_type": "Release", - "custom_install_script": "build_binaryen", - "custom_is_installed_script": "is_binaryen_installed", - "custom_uninstall_script": "uninstall_binaryen" - }, - { - "id": "binaryen", - "version": "master", - "bitness": 64, - "append_bitness": false, - "url": "https://github.com/WebAssembly/binaryen.git", - "git_branch": "master", - "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", - "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", - "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", - "cmake_build_type": "Release", - "custom_install_script": "build_binaryen", - "custom_is_installed_script": "is_binaryen_installed", - "custom_uninstall_script": "uninstall_binaryen" - }, - { - "id": "vs-tool", - "version": "0.9.0", - "windows_url": "vs-tool_0.9.0.zip", - "windows_install_path": "%MSBuildPlatformsDir%/Emscripten", - "is_old": true - }, - { - "id": "vs-tool", - "version": "0.9.1", - "windows_url": "vs-tool_0.9.1.zip", - "windows_install_path": "%MSBuildPlatformsDir%/Emscripten", - "is_old": true - }, - { - "id": "vs-tool", - "version": "0.9.2", - "windows_url": "vs-tool_0.9.2.zip", - "windows_install_path": "%MSBuildPlatformsDir%/Emscripten", - "is_old": true - }, - { - "id": "vs-tool", - "version": "0.9.3", - "windows_url": "vs-tool_0.9.3.zip", - "windows_install_path": "%MSBuildPlatformsDir%/Emscripten", - "is_old": true - }, - { - "id": "vs-tool", - "version": "0.9.4", - "windows_url": "vs-tool_0.9.4.zip", - "windows_install_path": "%MSBuildPlatformsDir%/Emscripten" - }, - { - "id": "crunch", - "version": "1.03", - "windows_url": "crunch_1.03.zip", - "activated_cfg": "CRUNCH='%installation_dir%/crunch.exe'", - "activated_path": "%installation_dir%" - }, - { - "id": "crunch", - "version": "1.04", - "osx_url": "crunch_osx_1.04.tar.gz", - "activated_cfg": "CRUNCH='%installation_dir%/crunch'", - "activated_path": "%installation_dir%" - }, - { - "id": "gnu", - "version": "2.5.4", - "windows_url": "grep-2.5.4.zip", - "activated_path": "%installation_dir%/bin" - }, - { - "id": "mingw", - "version": "4.6.2", - "bitness": 32, - "windows_url": "mingw_4.6.2_32bit.zip", - "activated_cfg": "MINGW_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%", - "is_old": true - }, - { - "id": "mingw", - "version": "7.1.0", - "bitness": 64, - "windows_url": "mingw_7.1.0_64bit.zip", - "activated_cfg": "MINGW_ROOT='%installation_dir%'", - "activated_path": "%installation_dir%/bin" - } - ], - - "sdks": [ - { - "version": "incoming", - "bitness": 32, - "uses": ["clang-incoming-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-incoming-32bit", "binaryen-master-32bit"], - "os": "win" - }, - { - "version": "wasm-master", - "bitness": 32, - "uses": ["upstream-clang-master-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-incoming-32bit", "binaryen-master-32bit"], - "os": "win" - }, - { - "version": "incoming", - "bitness": 64, - "uses": ["clang-incoming-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "win" - }, - { - "version": "wasm-master", - "bitness": 64, - "uses": ["upstream-clang-master-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "win" - }, - { - "version": "incoming", - "bitness": 64, - "uses": ["clang-incoming-64bit", "node-8.9.1-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "osx" - }, - { - "version": "wasm-master", - "bitness": 64, - "uses": ["upstream-clang-master-64bit", "node-8.9.1-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "osx" - }, - { - "version": "incoming", - "bitness": 32, - "uses": ["clang-incoming-32bit", "node-8.9.1-32bit", "emscripten-incoming-32bit", "binaryen-master-32bit"], - "os": "linux" - }, - { - "version": "wasm-master", - "bitness": 32, - "uses": ["upstream-clang-master-32bit", "node-8.9.1-32bit", "emscripten-incoming-32bit", "binaryen-master-32bit"], - "os": "linux" - }, - { - "version": "incoming", - "bitness": 64, - "uses": ["clang-incoming-64bit", "node-8.9.1-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "linux" - }, - { - "version": "wasm-master", - "bitness": 64, - "uses": ["upstream-clang-master-64bit", "node-8.9.1-64bit", "emscripten-incoming-64bit", "binaryen-master-64bit"], - "os": "linux" - }, - { - "version": "master", - "bitness": 32, - "uses": ["clang-master-32bit", "node-4.1.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-master-32bit", "binaryen-master-32bit"], - "os": "win" - }, - { - "version": "master", - "bitness": 64, - "uses": ["clang-master-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-master-64bit", "binaryen-master-64bit"], - "os": "win" - }, - { - "version": "master", - "bitness": 64, - "uses": ["clang-master-64bit", "node-4.1.1-64bit", "emscripten-master-64bit", "binaryen-master-64bit"], - "os": "osx" - }, - { - "version": "master", - "bitness": 32, - "uses": ["clang-master-32bit", "node-4.1.1-32bit", "emscripten-master-32bit", "binaryen-master-32bit"], - "os": "linux" - }, - { - "version": "master", - "bitness": 64, - "uses": ["clang-master-64bit", "node-4.1.1-64bit", "emscripten-master-64bit", "binaryen-master-64bit"], - "os": "linux" - }, - { - "version": "tag-%tag%", - "bitness": 32, - "uses": ["clang-tag-e%tag%-32bit", "node-4.1.1-32bit", "python-2.7.13.1-32bit", "java-7.45-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], - "os": "win", - "version_filter": [ - ["%tag%", "<=", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 64, - "uses": ["clang-tag-e%tag%-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "java-7.45-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], - "os": "win", - "version_filter": [ - ["%tag%", "<=", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 32, - "uses": ["clang-tag-e%tag%-32bit", "node-4.1.1-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], - "os": "linux", - "version_filter": [ - ["%tag%", "<=", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 64, - "uses": ["clang-tag-e%tag%-64bit", "node-4.1.1-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], - "os": "unix", - "version_filter": [ - ["%tag%", "<=", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 32, - "uses": ["clang-tag-e%tag%-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], - "os": "win", - "version_filter": [ - ["%tag%", ">", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 64, - "uses": ["clang-tag-e%tag%-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], - "os": "win", - "version_filter": [ - ["%tag%", ">", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 32, - "uses": ["clang-tag-e%tag%-32bit", "node-8.9.1-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], - "os": "linux", - "version_filter": [ - ["%tag%", ">", "1.37.22"] - ] - }, - { - "version": "tag-%tag%", - "bitness": 64, - "uses": ["clang-tag-e%tag%-64bit", "node-8.9.1-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], - "os": "unix", - "version_filter": [ - ["%tag%", ">", "1.37.22"] - ] - }, - { - "version": "nightly-%nightly-llvm-32bit%", - "bitness": 32, - "uses": ["clang-nightly-e%nightly-llvm-32bit%-32bit", "node-4.1.1-32bit", "python-2.7.13.1-32bit", "java-7.45-32bit", "emscripten-nightly-%nightly-llvm-32bit%"], - "os": "win", - "version_filter": [ - ["%nightly-llvm-32bit%", "<=", "1.37.22-2017_11_05"] - ] - }, - { - "version": "nightly-%nightly-llvm-64bit%", - "bitness": 64, - "uses": ["clang-nightly-e%nightly-llvm-64bit%-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "java-7.45-64bit", "emscripten-nightly-%nightly-llvm-64bit%"], - "os": "win", - "version_filter": [ - ["%nightly-llvm-64bit%", "<=", "1.37.22-2017_11_05"] - ] - }, - { - "version": "nightly-%nightly-llvm-32bit%", - "bitness": 32, - "uses": ["clang-nightly-e%nightly-llvm-32bit%-32bit", "node-4.1.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-nightly-%nightly-llvm-32bit%"], - "os": "win", - "version_filter": [ - ["%nightly-llvm-32bit%", ">", "1.37.22-2017_11_05"] - ] - }, - { - "version": "nightly-%nightly-llvm-64bit%", - "bitness": 64, - "uses": ["clang-nightly-e%nightly-llvm-64bit%-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-nightly-%nightly-llvm-64bit%"], - "os": "win", - "version_filter": [ - ["%nightly-llvm-64bit%", ">", "1.37.22-2017_11_05"] - ] - }, - { - "version": "nightly-%nightly-llvm-32bit%", - "bitness": 32, - "uses": ["clang-nightly-e%nightly-llvm-32bit%-32bit", "node-4.1.1-64bit", "emscripten-nightly-%nightly-llvm-32bit%"], - "os": "unix" - }, - { - "version": "nightly-%nightly-llvm-64bit%", - "bitness": 64, - "uses": ["clang-nightly-e%nightly-llvm-64bit%-64bit", "node-4.1.1-64bit", "emscripten-nightly-%nightly-llvm-64bit%"], - "os": "unix" - }, - { - "version": "1.5.6", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.1-32bit", "java-7.45-32bit", "emscripten-1.5.6"], - "os": "win", - "is_old": true - }, - { - "version": "1.5.6", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5-64bit", "java-7.45-64bit", "emscripten-1.5.6"], - "os": "win", - "is_old": true - }, - { - "version": "1.5.6", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.5.6"], - "os": "osx", - "is_old": true - }, - { - "version": "1.7.1", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.1-32bit", "java-7.45-32bit", "emscripten-1.7.1"], - "os": "win", - "is_old": true - }, - { - "version": "1.7.1", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5-64bit", "java-7.45-64bit", "emscripten-1.7.1"], - "os": "win", - "is_old": true - }, - { - "version": "1.7.1", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.7.1"], - "os": "osx", - "is_old": true - }, - { - "version": "1.7.8", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.3-32bit", "java-7.45-32bit", "emscripten-1.7.8"], - "os": "win", - "is_old": true - }, - { - "version": "1.7.8", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "java-7.45-64bit", "emscripten-1.7.8"], - "os": "win", - "is_old": true - }, - { - "version": "1.7.8", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.7.8"], - "os": "osx", - "is_old": true - }, - { - "version": "1.8.2", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.3-32bit", "emscripten-1.8.2"], - "os": "win", - "is_old": true - }, - { - "version": "1.8.2", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.8.2"], - "os": "win", - "is_old": true - }, - { - "version": "1.8.2", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.8.2"], - "os": "osx", - "is_old": true - }, - { - "version": "1.9.5", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.3-32bit", "emscripten-1.9.5"], - "os": "win", - "is_old": true - }, - { - "version": "1.9.5", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.9.5"], - "os": "win", - "is_old": true - }, - { - "version": "1.9.5", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.9.5"], - "os": "osx", - "is_old": true - }, - { - "version": "1.10.4", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.3-32bit", "emscripten-1.10.4"], - "os": "win", - "is_old": true - }, - { - "version": "1.10.4", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.10.4"], - "os": "win", - "is_old": true - }, - { - "version": "1.10.4", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.10.4"], - "os": "osx", - "is_old": true - }, - { - "version": "1.12.0", - "bitness": 32, - "uses": ["clang-3.2-32bit", "node-0.10.17-32bit", "python-2.7.5.3-32bit", "emscripten-1.12.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.12.0", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.12.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.12.0", - "bitness": 64, - "uses": ["clang-3.2-64bit", "node-0.10.18-64bit", "emscripten-1.12.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.13.0", - "bitness": 64, - "uses": ["clang-e1.13.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.13.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.13.0", - "bitness": 64, - "uses": ["clang-e1.13.0-64bit", "node-0.10.18-64bit", "emscripten-1.13.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.13.1", - "bitness": 64, - "uses": ["clang-e1.13.1-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.13.1"], - "os": "win", - "is_old": true - }, - { - "version": "1.13.1", - "bitness": 64, - "uses": ["clang-e1.13.1-64bit", "node-0.10.18-64bit", "emscripten-1.13.1"], - "os": "osx", - "is_old": true - }, - { - "version": "1.16.0", - "bitness": 64, - "uses": ["clang-e1.16.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.16.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.16.0", - "bitness": 64, - "uses": ["clang-e1.16.0-64bit", "node-0.10.18-64bit", "emscripten-1.16.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.21.0", - "bitness": 64, - "uses": ["clang-e1.21.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.21.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.21.0", - "bitness": 64, - "uses": ["clang-e1.21.0-64bit", "node-0.10.18-64bit", "emscripten-1.21.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.22.0", - "bitness": 64, - "uses": ["clang-e1.22.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.22.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.22.0", - "bitness": 64, - "uses": ["clang-e1.22.0-64bit", "node-0.10.18-64bit", "emscripten-1.22.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.25.0", - "bitness": 64, - "uses": ["clang-e1.25.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.25.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.25.0", - "bitness": 64, - "uses": ["clang-e1.25.0-64bit", "node-0.10.18-64bit", "emscripten-1.25.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.27.0", - "bitness": 64, - "uses": ["clang-e1.27.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.27.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.27.0", - "bitness": 64, - "uses": ["clang-e1.27.0-64bit", "node-0.10.18-64bit", "emscripten-1.27.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.29.0", - "bitness": 64, - "uses": ["clang-e1.29.0-64bit", "node-0.10.17-64bit", "python-2.7.5.3-64bit", "emscripten-1.29.0"], - "os": "win", - "is_old": true - }, - { - "version": "1.29.0", - "bitness": 64, - "uses": ["clang-e1.29.0-64bit", "node-0.10.18-64bit", "emscripten-1.29.0"], - "os": "osx", - "is_old": true - }, - { - "version": "1.30.0", - "bitness": 64, - "uses": ["clang-e1.30.0-64bit", "node-0.12.2-64bit", "python-2.7.5.3-64bit", "emscripten-1.30.0"], - "os": "win" - }, - { - "version": "1.30.0", - "bitness": 64, - "uses": ["clang-e1.30.0-64bit", "node-0.12.2-64bit", "emscripten-1.30.0"], - "os": "osx" - }, - { - "version": "1.34.1", - "bitness": 64, - "uses": ["clang-e1.34.1-64bit", "node-0.12.2-64bit", "python-2.7.5.3-64bit", "emscripten-1.34.1"], - "os": "win" - }, - { - "version": "1.34.1", - "bitness": 64, - "uses": ["clang-e1.34.1-64bit", "node-0.12.2-64bit", "emscripten-1.34.1"], - "os": "osx" - }, - { - "version": "1.35.0", - "bitness": 64, - "uses": ["clang-e1.35.0-64bit", "node-4.1.1-64bit", "python-2.7.5.3-64bit", "emscripten-1.35.0"], - "os": "win" - }, - { - "version": "1.35.0", - "bitness": 64, - "uses": ["clang-e1.35.0-64bit", "node-4.1.1-64bit", "emscripten-1.35.0"], - "os": "osx" - }, - { - "version": "1.37.1", - "bitness": 64, - "uses": ["clang-e1.37.1-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "emscripten-1.37.1"], - "os": "win" - }, - { - "version": "waterfall-upstream-%waterfall-lkgr%", - "bitness": 64, - "uses": ["waterfall-upstream-%waterfall-lkgr%-64bit", "node-8.9.1-64bit"], - "os": "linux" - }, - { - "version": "waterfall-fastcomp-%waterfall-lkgr%", - "bitness": 64, - "uses": ["waterfall-fastcomp-%waterfall-lkgr%-64bit", "node-8.9.1-64bit"], - "os": "linux" - }, - { - "version": "releases-upstream-%releases-tag%", - "bitness": 64, - "uses": ["releases-upstream-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "linux" - }, - { - "version": "releases-upstream-%releases-tag%", - "bitness": 64, - "uses": ["releases-upstream-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "osx" - }, - { - "version": "releases-upstream-%releases-tag%", - "bitness": 64, - "uses": ["releases-upstream-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "win" - }, - { - "version": "releases-fastcomp-%releases-tag%", - "bitness": 64, - "uses": ["releases-fastcomp-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "linux" - }, - { - "version": "releases-fastcomp-%releases-tag%", - "bitness": 64, - "uses": ["releases-fastcomp-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "osx" - }, - { - "version": "releases-fastcomp-%releases-tag%", - "bitness": 64, - "uses": ["releases-fastcomp-%releases-tag%-64bit", "node-8.9.1-64bit"], - "os": "win" - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-4.1.1-32bit", "python-2.7.13.1-32bit", "java-7.45-32bit", "emscripten-%precompiled_tag32%"], - "os": "win", - "version_filter": [ - ["%precompiled_tag32%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-4.1.1-64bit", "python-2.7.13.1-64bit", "java-7.45-64bit", "emscripten-%precompiled_tag64%"], - "os": "win", - "version_filter": [ - ["%precompiled_tag64%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-4.1.1-32bit", "emscripten-%precompiled_tag32%"], - "os": "linux", - "version_filter": [ - ["%precompiled_tag32%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-4.1.1-64bit", "emscripten-%precompiled_tag64%"], - "os": "linux", - "version_filter": [ - ["%precompiled_tag64%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-4.1.1-32bit", "emscripten-%precompiled_tag32%"], - "os": "osx", - "version_filter": [ - ["%precompiled_tag32%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-4.1.1-64bit", "emscripten-%precompiled_tag64%"], - "os": "osx", - "version_filter": [ - ["%precompiled_tag64%", "<=", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-%precompiled_tag32%"], - "os": "win", - "version_filter": [ - ["%precompiled_tag32%", ">", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-%precompiled_tag64%"], - "os": "win", - "version_filter": [ - ["%precompiled_tag64%", ">", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "emscripten-%precompiled_tag32%"], - "os": "linux", - "version_filter": [ - ["%precompiled_tag32%", ">", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "emscripten-%precompiled_tag64%"], - "os": "linux", - "version_filter": [ - ["%precompiled_tag64%", ">", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag32%", - "bitness": 32, - "uses": ["clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "emscripten-%precompiled_tag32%"], - "os": "osx", - "version_filter": [ - ["%precompiled_tag32%", ">", "1.37.22"] - ] - }, - { - "version": "%precompiled_tag64%", - "bitness": 64, - "uses": ["clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "emscripten-%precompiled_tag64%"], - "os": "osx", - "version_filter": [ - ["%precompiled_tag64%", ">", "1.37.22"] - ] - } - ] -} +{ + "tools": [ + { + "id": "llvm-git", + "version": "main", + "bitness": 32, + "install_path": "llvm/git", + "git_branch": "main", + "url": "https://github.com/llvm/llvm-project.git", + "custom_install_script": "build_llvm", + "only_supports_wasm": true, + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=1", + "cmake_build_type": "Release" + }, + { + "id": "llvm-git", + "version": "main", + "bitness": 64, + "install_path": "llvm/git", + "git_branch": "main", + "url": "https://github.com/llvm/llvm-project.git", + "custom_install_script": "build_llvm", + "only_supports_wasm": true, + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=1", + "cmake_build_type": "Release" + }, + + { + "id": "clang", + "version": "tag-e%tag%", + "bitness": 32, + "append_bitness": false, + "windows_url": "https://github.com/emscripten-core/emscripten-fastcomp/archive/%tag%.zip", + "unix_url": "https://github.com/emscripten-core/emscripten-fastcomp/archive/%tag%.tar.gz", + "windows_clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang/archive/%tag%.zip", + "unix_clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang/archive/%tag%.tar.gz", + "custom_install_script": "build_fastcomp", + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=0", + "cmake_build_type": "Release" + }, + { + "id": "fastcomp-clang", + "version": "tag-e%tag%", + "bitness": 64, + "append_bitness": false, + "windows_url": "https://github.com/emscripten-core/emscripten-fastcomp/archive/%tag%.zip", + "unix_url": "https://github.com/emscripten-core/emscripten-fastcomp/archive/%tag%.tar.gz", + "windows_clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang/archive/%tag%.zip", + "unix_clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang/archive/%tag%.tar.gz", + "custom_install_script": "build_fastcomp", + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=0", + "cmake_build_type": "Release" + }, + { + "id": "fastcomp-clang", + "version": "master", + "bitness": 32, + "install_path": "clang/fastcomp", + "url": "https://github.com/emscripten-core/emscripten-fastcomp.git", + "clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang.git", + "git_branch": "master", + "custom_install_script": "build_fastcomp", + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=0", + "cmake_build_type": "Release" + }, + { + "id": "fastcomp-clang", + "version": "master", + "bitness": 64, + "install_path": "clang/fastcomp", + "git_branch": "master", + "url": "https://github.com/emscripten-core/emscripten-fastcomp.git", + "clang_url": "https://github.com/emscripten-core/emscripten-fastcomp-clang.git", + "custom_install_script": "build_fastcomp", + "activated_path": "%installation_dir%/%fastcomp_build_bin_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%/%fastcomp_build_bin_dir%'", + "activated_env": "LLVM_ROOT=%installation_dir%/%fastcomp_build_bin_dir%;EMCC_WASM_BACKEND=0", + "cmake_build_type": "Release" + }, + + { + "id": "releases", + "version": "upstream-%releases-tag%", + "bitness": 64, + "arch": "x86_64", + "linux_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/%releases-tag%/wasm-binaries.tbz2", + "macos_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/mac/%releases-tag%/wasm-binaries.tbz2", + "windows_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/%releases-tag%/wasm-binaries.zip", + "zipfile_prefix": "%releases-tag%-", + "install_path": "upstream", + "activated_path": "%installation_dir%/emscripten", + "activated_cfg": "LLVM_ROOT='%installation_dir%/bin';BINARYEN_ROOT='%installation_dir%';EMSCRIPTEN_ROOT='%installation_dir%/emscripten'", + "emscripten_releases_hash": "%releases-tag%" + }, + { + "id": "releases", + "version": "fastcomp-%releases-tag%", + "bitness": 64, + "arch": "x86_64", + "linux_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/linux/%releases-tag%/wasm-binaries.tbz2", + "macos_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/mac/%releases-tag%/wasm-binaries.tbz2", + "windows_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/win/%releases-tag%/wasm-binaries.zip", + "zipfile_prefix": "%releases-tag%-", + "install_path": "fastcomp", + "activated_path": "%installation_dir%/emscripten", + "activated_cfg": "LLVM_ROOT='%installation_dir%/fastcomp/bin';BINARYEN_ROOT='%installation_dir%';EMSCRIPTEN_ROOT='%installation_dir%/emscripten';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/bin/optimizer%.exe%'", + "emscripten_releases_hash": "%releases-tag%" + }, + + { + "id": "clang", + "version": "e%precompiled_tag32%", + "bitness": 32, + "arch": "x86", + "windows_url": "llvm/tag/win_64bit/emscripten-llvm-e%precompiled_tag32%.zip", + "macos_url": "llvm/tag/osx_64bit/emscripten-llvm-e%precompiled_tag32%.tar.gz", + "linux_url": "llvm/tag/linux_64bit/emscripten-llvm-e%precompiled_tag32%.tar.gz", + "activated_path": "%installation_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%';BINARYEN_ROOT='%installation_dir%/binaryen'", + "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%;BINARYEN_ROOT=%installation_dir%/binaryen;EMCC_WASM_BACKEND=0" + }, + { + "id": "fastcomp-clang", + "version": "e%precompiled_tag64%", + "bitness": 64, + "arch": "x86_64", + "windows_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/old/win/emscripten-llvm-e%precompiled_tag64%.zip", + "macos_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/old/mac/emscripten-llvm-e%precompiled_tag64%.tar.gz", + "linux_url": "https://storage.googleapis.com/webassembly/emscripten-releases-builds/old/linux/emscripten-llvm-e%precompiled_tag64%.tar.gz", + "activated_path": "%installation_dir%", + "activated_cfg": "LLVM_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%/optimizer%.exe%';BINARYEN_ROOT='%installation_dir%/binaryen'", + "activated_env": "LLVM_ROOT=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%/optimizer%.exe%;BINARYEN_ROOT=%installation_dir%/binaryen;EMCC_WASM_BACKEND=0" + }, + { + "id": "node", + "version": "8.9.1", + "bitness": 32, + "arch": "x86", + "windows_url": "node-v8.9.1-win-x86.zip", + "linux_url": "node-v8.9.1-linux-x86.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%", + "is_old": true + }, + { + "id": "node", + "version": "8.9.1", + "arch": "arm", + "bitness": 32, + "linux_url": "https://nodejs.org/dist/v8.9.1/node-v8.9.1-linux-armv7l.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%", + "is_old": true + }, + { + "id": "node", + "version": "8.9.1", + "bitness": 64, + "arch": "x86_64", + "macos_url": "node-v8.9.1-darwin-x64.tar.gz", + "windows_url": "node-v8.9.1-win-x64.zip", + "linux_url": "node-v8.9.1-linux-x64.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%", + "is_old": true + }, + { + "id": "node", + "version": "8.9.1", + "arch": "aarch64", + "bitness": 64, + "linux_url": "node-v8.9.1-linux-arm64.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%", + "is_old": true + }, + { + "id": "node", + "version": "14.15.5", + "bitness": 32, + "arch": "x86", + "windows_url": "node-v14.15.5-win-x86.zip", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" + }, + { + "id": "node", + "version": "14.15.5", + "arch": "arm", + "bitness": 32, + "linux_url": "node-v14.15.5-linux-armv7l.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" + }, + { + "id": "node", + "version": "14.15.5", + "bitness": 64, + "arch": "x86_64", + "macos_url": "node-v14.15.5-darwin-x64.tar.gz", + "windows_url": "node-v14.15.5-win-x64.zip", + "linux_url": "node-v14.15.5-linux-x64.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" + }, + { + "id": "node", + "version": "14.15.5", + "arch": "aarch64", + "bitness": 64, + "macos_url": "node-v14.15.5-darwin-x64.tar.gz", + "linux_url": "node-v14.15.5-linux-arm64.tar.xz", + "activated_path": "%installation_dir%/bin", + "activated_cfg": "NODE_JS='%installation_dir%/bin/node%.exe%'", + "activated_env": "EMSDK_NODE=%installation_dir%/bin/node%.exe%" + }, + { + "id": "python", + "version": "2.7.13.1", + "bitness": 32, + "arch": "x86", + "windows_url": "WinPython-32bit-2.7.13.1Zero.zip", + "activated_cfg": "PYTHON='%installation_dir%/python-2.7.13/python%.exe%'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python-2.7.13/python%.exe%", + "is_old": true + }, + { + "id": "python", + "version": "2.7.13.1", + "bitness": 64, + "arch": "x86_64", + "windows_url": "WinPython-64bit-2.7.13.1Zero.zip", + "activated_cfg": "PYTHON='%installation_dir%/python-2.7.13.amd64/python%.exe%'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python-2.7.13.amd64/python%.exe%", + "is_old": true + }, + { + "id": "python", + "version": "3.7.4", + "bitness": 32, + "arch": "x86", + "windows_url": "python-3.7.4-embed-win32-patched.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe", + "is_old": true + }, + { + "id": "python", + "version": "3.7.4", + "bitness": 64, + "arch": "x86_64", + "windows_url": "python-3.7.4-embed-amd64-patched.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe", + "is_old": true + }, + { + "id": "python", + "version": "3.7.4-pywin32", + "bitness": 32, + "arch": "x86", + "windows_url": "python-3.7.4-embed-win32+pywin32.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe", + "is_old": true + }, + { + "id": "python", + "version": "3.7.4-pywin32", + "bitness": 64, + "arch": "x86_64", + "windows_url": "python-3.7.4-embed-amd64+pywin32.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe", + "is_old": true + }, + { + "id": "python", + "version": "3.7.4-2", + "bitness": 64, + "arch": "x86_64", + "macos_url": "python-3.7.4-2-macos.tar.gz", + "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.7/site-packages/certifi/cacert.pem", + "is_old": true + }, + { + "id": "python", + "version": "3.9.2-1", + "bitness": 64, + "arch": "x86_64", + "windows_url": "python-3.9.2-1-embed-amd64+pywin32.zip", + "activated_cfg": "PYTHON='%installation_dir%/python.exe'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/python.exe" + }, + { + "id": "python", + "version": "3.9.2-1", + "bitness": 64, + "arch": "x86_64", + "macos_url": "python-3.9.2-1-macos-x86_64.tar.gz", + "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.9/site-packages/certifi/cacert.pem" + }, + { + "id": "python", + "version": "3.9.2-1", + "bitness": 64, + "arch": "aarch64", + "macos_url": "python-3.9.2-1-macos-arm64.tar.gz", + "activated_cfg": "PYTHON='%installation_dir%/bin/python3'", + "activated_env": "EMSDK_PYTHON=%installation_dir%/bin/python3;SSL_CERT_FILE=%installation_dir%/lib/python3.9/site-packages/certifi/cacert.pem" + }, + { + "id": "java", + "version": "8.152", + "bitness": 32, + "arch": "x86", + "windows_url": "portable_jre_8_update_152_32bit.zip", + "activated_env": "JAVA_HOME=%installation_dir%", + "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" + }, + { + "id": "java", + "version": "8.152", + "bitness": 64, + "arch": "x86_64", + "windows_url": "portable_jre_8_update_152_64bit.zip", + "activated_env": "JAVA_HOME=%installation_dir%", + "activated_cfg": "JAVA='%installation_dir%/bin/java%.exe%'" + }, + { + "id": "emscripten", + "version": "tag-%tag%", + "bitness": 32, + "append_bitness": false, + "windows_url": "https://github.com/emscripten-core/emscripten/archive/%tag%.zip", + "unix_url": "https://github.com/emscripten-core/emscripten/archive/%tag%.tar.gz", + "zipfile_prefix": "emscripten-e", + "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", + "activated_path": "%installation_dir%", + "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_32bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", + "cmake_build_type": "Release", + "custom_install_script": "emscripten_post_install", + "custom_is_installed_script": "is_optimizer_installed", + "custom_uninstall_script": "uninstall_optimizer" + }, + { + "id": "emscripten", + "version": "tag-%tag%", + "bitness": 64, + "append_bitness": false, + "windows_url": "https://github.com/emscripten-core/emscripten/archive/%tag%.zip", + "unix_url": "https://github.com/emscripten-core/emscripten/archive/%tag%.tar.gz", + "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%';EMSCRIPTEN_NATIVE_OPTIMIZER='%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%'", + "activated_path": "%installation_dir%", + "activated_env": "EMSCRIPTEN=%installation_dir%;EMSCRIPTEN_NATIVE_OPTIMIZER=%installation_dir%%generator_prefix%_64bit_optimizer/%cmake_build_type_on_win%optimizer%.exe%", + "cmake_build_type": "Release", + "custom_install_script": "emscripten_post_install", + "custom_is_installed_script": "is_optimizer_installed", + "custom_uninstall_script": "uninstall_optimizer" + }, + { + "id": "emscripten", + "version": "%precompiled_tag%", + "windows_url": "https://github.com/emscripten-core/emscripten/archive/%precompiled_tag%.zip", + "unix_url": "https://github.com/emscripten-core/emscripten/archive/%precompiled_tag%.tar.gz", + "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", + "activated_path": "%installation_dir%", + "activated_env": "EMSCRIPTEN=%installation_dir%" + }, + { + "id": "binaryen", + "version": "tag-%binaryen_tag%", + "bitness": 32, + "append_bitness": false, + "windows_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.zip", + "unix_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.tar.gz", + "zipfile_prefix": "binaryen-e", + "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", + "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", + "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", + "cmake_build_type": "Release", + "custom_install_script": "build_binaryen", + "custom_is_installed_script": "is_binaryen_installed", + "custom_uninstall_script": "uninstall_binaryen" + }, + { + "id": "binaryen", + "version": "tag-%binaryen_tag%", + "bitness": 64, + "append_bitness": false, + "windows_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.zip", + "unix_url": "https://github.com/WebAssembly/binaryen/archive/%binaryen_tag%.tar.gz", + "zipfile_prefix": "binaryen-e", + "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", + "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", + "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", + "cmake_build_type": "Release", + "custom_install_script": "build_binaryen", + "custom_is_installed_script": "is_binaryen_installed", + "custom_uninstall_script": "uninstall_binaryen" + }, + { + "id": "emscripten", + "version": "main", + "bitness": 32, + "append_bitness": false, + "url": "https://github.com/emscripten-core/emscripten.git", + "git_branch": "main", + "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", + "activated_path": "%installation_dir%", + "activated_env": "EMSCRIPTEN=%installation_dir%", + "cmake_build_type": "Release", + "custom_install_script": "emscripten_npm_install" + }, + { + "id": "emscripten", + "version": "main", + "bitness": 64, + "append_bitness": false, + "url": "https://github.com/emscripten-core/emscripten.git", + "git_branch": "main", + "activated_cfg": "EMSCRIPTEN_ROOT='%installation_dir%'", + "activated_path": "%installation_dir%", + "activated_env": "EMSCRIPTEN=%installation_dir%", + "cmake_build_type": "Release", + "custom_install_script": "emscripten_npm_install" + }, + { + "id": "binaryen", + "version": "main", + "bitness": 32, + "append_bitness": false, + "url": "https://github.com/WebAssembly/binaryen.git", + "git_branch": "main", + "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_32bit_binaryen'", + "activated_path": "%installation_dir%%generator_prefix%_32bit_binaryen/bin", + "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_32bit_binaryen", + "cmake_build_type": "Release", + "custom_install_script": "build_binaryen", + "custom_is_installed_script": "is_binaryen_installed", + "custom_uninstall_script": "uninstall_binaryen" + }, + { + "id": "binaryen", + "version": "main", + "bitness": 64, + "append_bitness": false, + "url": "https://github.com/WebAssembly/binaryen.git", + "git_branch": "main", + "activated_cfg": "BINARYEN_ROOT='%installation_dir%%generator_prefix%_64bit_binaryen'", + "activated_path": "%installation_dir%%generator_prefix%_64bit_binaryen/bin", + "activated_env": "BINARYEN_ROOT=%installation_dir%%generator_prefix%_64bit_binaryen", + "cmake_build_type": "Release", + "custom_install_script": "build_binaryen", + "custom_is_installed_script": "is_binaryen_installed", + "custom_uninstall_script": "uninstall_binaryen" + }, + { + "id": "gnu", + "version": "2.5.4", + "windows_url": "grep-2.5.4.zip", + "activated_path": "%installation_dir%/bin" + }, + { + "id": "mingw", + "version": "4.6.2", + "bitness": 32, + "windows_url": "mingw_4.6.2_32bit.zip", + "activated_cfg": "MINGW_ROOT='%installation_dir%'", + "activated_path": "%installation_dir%", + "is_old": true + }, + { + "id": "mingw", + "version": "7.1.0", + "bitness": 64, + "windows_url": "mingw_7.1.0_64bit.zip", + "activated_cfg": "MINGW_ROOT='%installation_dir%'", + "activated_path": "%installation_dir%/bin" + }, + { + "id": "ninja", + "version": "git-release", + "bitness": 64, + "url": "https://github.com/ninja-build/ninja.git", + "git_branch": "release", + "activated_cfg": "NINJA=%installation_dir%/bin", + "activated_path": "%installation_dir%/bin", + "cmake_build_type": "Release", + "custom_install_script": "build_ninja" + }, + { + "id": "ccache", + "version": "git-emscripten", + "bitness": 64, + "url": "https://github.com/juj/ccache.git", + "git_branch": "emscripten", + "activated_path": "%installation_dir%/bin", + "activated_env": "_EMCC_CCACHE=1;CCACHE_CONFIGPATH=%installation_dir%/emcc_ccache.conf", + "cmake_build_type": "Release", + "custom_install_script": "build_ccache" + } + ], + + "sdks": [ + { + "version": "upstream-main", + "bitness": 64, + "uses": ["python-3.9.2-1-64bit", "llvm-git-main-64bit", "node-14.15.5-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], + "os": "win" + }, + { + "version": "upstream-main", + "bitness": 64, + "uses": ["python-3.9.2-1-64bit", "llvm-git-main-64bit", "node-14.15.5-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], + "os": "macos" + }, + { + "version": "upstream-main", + "bitness": 64, + "uses": ["llvm-git-main-64bit", "node-14.15.5-64bit", "emscripten-main-64bit", "binaryen-main-64bit"], + "os": "linux" + }, + { + "version": "upstream-main", + "bitness": 32, + "uses": ["llvm-git-main-32bit", "emscripten-main-32bit", "binaryen-main-32bit"], + "os": "linux" + }, + { + "version": "fastcomp-tag-%tag%", + "bitness": 32, + "uses": ["fastcomp-clang-tag-e%tag%-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], + "os": "win", + "version_filter": [ + ["%tag%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-tag-%tag%", + "bitness": 64, + "uses": ["fastcomp-clang-tag-e%tag%-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], + "os": "win", + "version_filter": [ + ["%tag%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-tag-%tag%", + "bitness": 32, + "uses": ["fastcomp-clang-tag-e%tag%-32bit", "node-8.9.1-32bit", "emscripten-tag-%tag%-32bit", "binaryen-tag-%tag%-32bit"], + "os": "linux", + "version_filter": [ + ["%tag%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-tag-%tag%", + "bitness": 64, + "uses": ["fastcomp-clang-tag-e%tag%-64bit", "node-8.9.1-64bit", "emscripten-tag-%tag%-64bit", "binaryen-tag-%tag%-64bit"], + "os": "unix", + "version_filter": [ + ["%tag%", ">", "1.37.22"] + ] + }, + { + "version": "releases-upstream-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "releases-upstream-%releases-tag%-64bit"], + "os": "linux", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "releases-upstream-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "python-3.9.2-1-64bit", "releases-upstream-%releases-tag%-64bit"], + "os": "macos", + "arch": "x86_64", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "releases-upstream-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "python-3.9.2-1-64bit", "java-8.152-64bit", "releases-upstream-%releases-tag%-64bit"], + "os": "win", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "releases-fastcomp-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "releases-fastcomp-%releases-tag%-64bit"], + "os": "linux", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "releases-fastcomp-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "python-3.7.4-2-64bit", "releases-fastcomp-%releases-tag%-64bit"], + "os": "macos", + "arch": "x86_64", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "releases-fastcomp-%releases-tag%", + "bitness": 64, + "uses": ["node-14.15.5-64bit", "python-3.7.4-pywin32-64bit", "java-8.152-64bit", "releases-fastcomp-%releases-tag%-64bit"], + "os": "win", + "custom_install_script": "emscripten_npm_install" + }, + { + "version": "fastcomp-%precompiled_tag32%", + "bitness": 32, + "uses": ["fastcomp-clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "python-2.7.13.1-32bit", "java-8.152-32bit", "emscripten-%precompiled_tag32%"], + "os": "win", + "version_filter": [ + ["%precompiled_tag32%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-%precompiled_tag64%", + "bitness": 64, + "uses": ["fastcomp-clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "python-2.7.13.1-64bit", "java-8.152-64bit", "emscripten-%precompiled_tag64%"], + "os": "win", + "version_filter": [ + ["%precompiled_tag64%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-%precompiled_tag32%", + "bitness": 32, + "uses": ["fastcomp-clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "emscripten-%precompiled_tag32%"], + "os": "linux", + "version_filter": [ + ["%precompiled_tag32%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-%precompiled_tag64%", + "bitness": 64, + "uses": ["fastcomp-clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "emscripten-%precompiled_tag64%"], + "os": "linux", + "version_filter": [ + ["%precompiled_tag64%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-%precompiled_tag32%", + "bitness": 32, + "uses": ["fastcomp-clang-e%precompiled_tag32%-32bit", "node-8.9.1-32bit", "python-3.7.4-2-64bit", "emscripten-%precompiled_tag32%"], + "os": "macos", + "arch": "x86_64", + "version_filter": [ + ["%precompiled_tag32%", ">", "1.37.22"] + ] + }, + { + "version": "fastcomp-%precompiled_tag64%", + "bitness": 64, + "uses": ["fastcomp-clang-e%precompiled_tag64%-64bit", "node-8.9.1-64bit", "python-3.7.4-2-64bit", "emscripten-%precompiled_tag64%"], + "os": "macos", + "arch": "x86_64", + "version_filter": [ + ["%precompiled_tag64%", ">", "1.37.22"] + ] + } + ] +} diff --git a/binaryen-tags.txt b/legacy-binaryen-tags.txt similarity index 100% rename from binaryen-tags.txt rename to legacy-binaryen-tags.txt diff --git a/emscripten-tags.txt b/legacy-emscripten-tags.txt similarity index 100% rename from emscripten-tags.txt rename to legacy-emscripten-tags.txt diff --git a/llvm-tags-64bit.txt b/llvm-tags-64bit.txt new file mode 100644 index 0000000000..7920f38fdc --- /dev/null +++ b/llvm-tags-64bit.txt @@ -0,0 +1,75 @@ +emscripten-llvm-e1.38.9.tar.gz +emscripten-llvm-e1.38.8.tar.gz +emscripten-llvm-e1.38.7.tar.gz +emscripten-llvm-e1.38.6.tar.gz +emscripten-llvm-e1.38.5.tar.gz +emscripten-llvm-e1.38.4.tar.gz +emscripten-llvm-e1.38.31.tar.gz +emscripten-llvm-e1.38.30.tar.gz +emscripten-llvm-e1.38.3.tar.gz +emscripten-llvm-e1.38.29.tar.gz +emscripten-llvm-e1.38.28.tar.gz +emscripten-llvm-e1.38.27.tar.gz +emscripten-llvm-e1.38.26.tar.gz +emscripten-llvm-e1.38.25.tar.gz +emscripten-llvm-e1.38.24.tar.gz +emscripten-llvm-e1.38.23.tar.gz +emscripten-llvm-e1.38.22.tar.gz +emscripten-llvm-e1.38.21.tar.gz +emscripten-llvm-e1.38.20.tar.gz +emscripten-llvm-e1.38.2.tar.gz +emscripten-llvm-e1.38.19.tar.gz +emscripten-llvm-e1.38.18.tar.gz +emscripten-llvm-e1.38.17.tar.gz +emscripten-llvm-e1.38.16.tar.gz +emscripten-llvm-e1.38.15.tar.gz +emscripten-llvm-e1.38.14.tar.gz +emscripten-llvm-e1.38.13.tar.gz +emscripten-llvm-e1.38.12.tar.gz +emscripten-llvm-e1.38.11.tar.gz +emscripten-llvm-e1.38.10.tar.gz +emscripten-llvm-e1.38.1.tar.gz +emscripten-llvm-e1.38.0.tar.gz +emscripten-llvm-e1.37.9.tar.gz +emscripten-llvm-e1.37.8.tar.gz +emscripten-llvm-e1.37.7.tar.gz +emscripten-llvm-e1.37.6.tar.gz +emscripten-llvm-e1.37.5.tar.gz +emscripten-llvm-e1.37.40.tar.gz +emscripten-llvm-e1.37.4.tar.gz +emscripten-llvm-e1.37.39.tar.gz +emscripten-llvm-e1.37.38.tar.gz +emscripten-llvm-e1.37.37.tar.gz +emscripten-llvm-e1.37.36.tar.gz +emscripten-llvm-e1.37.35.tar.gz +emscripten-llvm-e1.37.34.tar.gz +emscripten-llvm-e1.37.33.tar.gz +emscripten-llvm-e1.37.32.tar.gz +emscripten-llvm-e1.37.31.tar.gz +emscripten-llvm-e1.37.30.tar.gz +emscripten-llvm-e1.37.3.tar.gz +emscripten-llvm-e1.37.29.tar.gz +emscripten-llvm-e1.37.28.tar.gz +emscripten-llvm-e1.37.27.tar.gz +emscripten-llvm-e1.37.26.tar.gz +emscripten-llvm-e1.37.25.tar.gz +emscripten-llvm-e1.37.24.tar.gz +emscripten-llvm-e1.37.23.tar.gz +emscripten-llvm-e1.37.22.tar.gz +emscripten-llvm-e1.37.21.tar.gz +emscripten-llvm-e1.37.20.tar.gz +emscripten-llvm-e1.37.2.tar.gz +emscripten-llvm-e1.37.19.tar.gz +emscripten-llvm-e1.37.18.tar.gz +emscripten-llvm-e1.37.17.tar.gz +emscripten-llvm-e1.37.16.tar.gz +emscripten-llvm-e1.37.15.tar.gz +emscripten-llvm-e1.37.14.tar.gz +emscripten-llvm-e1.37.13.tar.gz +emscripten-llvm-e1.37.12.tar.gz +emscripten-llvm-e1.37.11.tar.gz +emscripten-llvm-e1.37.10.tar.gz +emscripten-llvm-e1.37.1.tar.gz +emscripten-llvm-e1.37.0.tar.gz +emscripten-llvm-e1.36.14.tar.gz +emscripten-llvm-e1.36.13.tar.gz diff --git a/scripts/create_release.py b/scripts/create_release.py new file mode 100755 index 0000000000..b84a43284f --- /dev/null +++ b/scripts/create_release.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +import json +import os +import subprocess +import sys +from collections import OrderedDict + +script_dir = os.path.dirname(os.path.abspath(__file__)) +root_dir = os.path.dirname(script_dir) +sys.path.append(root_dir) + +import emsdk # noqa + + +def version_to_list(version_string): + return [int(part) for part in version_string.split('.')] + + +def main(args): + if subprocess.check_output(['git', 'status', '--porcelain'], cwd=root_dir).strip(): + print('tree is not clean') + sys.exit(1) + + release_info = emsdk.load_releases_info() + new_version = version_to_list(release_info['latest']) + new_version[-1] += 1 + branch_name = 'version_%s' % '_'.join(str(part) for part in new_version) + + # Create a new git branch + subprocess.check_call(['git', 'checkout', '-b', branch_name], cwd=root_dir) + + new_version = '.'.join(str(part) for part in new_version) + new_hash = emsdk.get_emscripten_releases_tot() + print('Creating new release: %s -> %s' % (new_version, new_hash)) + release_info['releases'][new_version] = new_hash + releases = [(k, v) for k, v in release_info['releases'].items()] + releases.sort(key=lambda pair: version_to_list(pair[0])) + + release_info['releases'] = OrderedDict(reversed(releases)) + release_info['latest'] = new_version + + with open(os.path.join(root_dir, 'emscripten-releases-tags.txt'), 'w') as f: + f.write(json.dumps(release_info, indent=2)) + f.write('\n') + + subprocess.check_call(os.path.join(script_dir, 'update_bazel_workspace.sh'), cwd=root_dir) + + # Create auto-generated changes to the new git branch + subprocess.check_call(['git', 'add', '-u', '.'], cwd=root_dir) + subprocess.check_call(['git', 'commit', '-m', new_version], cwd=root_dir) + + print('New relase created in branch: `%s`' % branch_name) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/scripts/update_bazel_workspace.sh b/scripts/update_bazel_workspace.sh new file mode 100755 index 0000000000..b9b7bd278a --- /dev/null +++ b/scripts/update_bazel_workspace.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# This script will update emsdk/bazel/WORKSPACE to the latest version of +# emscripten. It reads emsdk/emscripten-releases-tags.txt to get the latest +# version number. Then, it downloads the prebuilts for that version and computes +# the sha256sum for the archive. It then puts all this information into the +# emsdk/bazel/WORKSPACE file. + +ERR=0 +# Attempt to change to the emsdk root directory +cd $(dirname $0)/.. + +# If the previous command succeeded. We are in the emsdk root. Check to make +# sure the files and directories we need are present. +if [[ $? = 0 ]]; then + if [[ ! -f emscripten-releases-tags.txt ]]; then + echo "Cannot find emscripten-releases-tags.txt." + ERR=1 + fi + + if [[ ! -d bazel ]]; then + echo "Cannot find the bazel directory." + ERR=1 + elif [[ ! -f bazel/WORKSPACE ]]; then + echo "Cannot find bazel/WORKSPACE." + ERR=1 + fi +else + ERR=1 +fi + +if [[ $ERR = 1 ]]; then + echo "Unable to cd into the emsdk root directory." + exit 1 +fi + +URL1=https://storage.googleapis.com/webassembly/emscripten-releases-builds/ +URL2=/wasm-binaries.tbz2 + +# Get commit hash for $1 version +get_hash () { + echo $(grep "$1" emscripten-releases-tags.txt | grep -v latest | cut -f4 -d\") +} + +# Get sha256 for $1 os $2 hash +get_sha () { + echo $(curl "${URL1}$1/$2${URL2}" 2>/dev/null | sha256sum | awk '{print $1}') +} + +# Assemble dictionary line +revisions_item () { + hash=$(get_hash $1) + echo \ + "\ \"$1\": struct(\n" \ + "\ hash = \"$(get_hash ${hash})\",\n" \ + "\ sha_linux = \"$(get_sha linux ${hash})\",\n" \ + "\ sha_mac = \"$(get_sha mac ${hash})\",\n" \ + "\ sha_win = \"$(get_sha win ${hash})\",\n" \ + "\ )," +} + +append_revision () { + sed -i "5 i $(revisions_item $1)" bazel/revisions.bzl +} + +# Get the latest version number from emscripten-releases-tag.txt. +VER=$(grep -oP '(?<=latest\": \")([\d\.]+)(?=\")' \ + emscripten-releases-tags.txt \ + | sed --expression "s/\./\\\./g") + +append_revision ${VER} + +echo "Done!" diff --git a/scripts/update_node.py b/scripts/update_node.py new file mode 100755 index 0000000000..45adce9d5d --- /dev/null +++ b/scripts/update_node.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# Copyright 2020 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +"""Updates the node binaries that we cache store at +http://storage.google.com/webassembly. + +For the windows version we also alter the directory layout to add the 'bin' +direcotry. +""" + +import urllib.request +import subprocess +import os +import shutil + +version = '14.15.5' +base = 'https://nodejs.org/dist/latest-v14.x/' +upload_base = 'gs://webassembly/emscripten-releases-builds/deps/' + +suffixes = [ + '-win-x64.zip', + '-darwin-x64.tar.gz', + '-linux-x64.tar.xz', + '-linux-arm64.tar.xz', + '-linux-armv7l.tar.xz', +] + +for suffix in suffixes: + filename = 'node-v%s%s' % (version, suffix) + download_url = base + filename + print('Downloading: ' + download_url) + urllib.request.urlretrieve(download_url, filename) + + if '-win-' in suffix: + subprocess.check_call(['unzip', '-q', filename]) + dirname = os.path.splitext(os.path.basename(filename))[0] + shutil.move(dirname, 'bin') + os.mkdir(dirname) + shutil.move('bin', dirname) + os.remove(filename) + subprocess.check_call(['zip', '-rq', filename, dirname]) + shutil.rmtree(dirname) + + upload_url = upload_base + filename + print('Uploading: ' + upload_url) + cmd = ['gsutil', 'cp', '-n', filename, upload_url] + print(' '.join(cmd)) + subprocess.check_call(cmd) + os.remove(filename) diff --git a/scripts/update_python.py b/scripts/update_python.py new file mode 100755 index 0000000000..390aa71521 --- /dev/null +++ b/scripts/update_python.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +# Copyright 2020 The Emscripten Authors. All rights reserved. +# Emscripten is available under two separate licenses, the MIT license and the +# University of Illinois/NCSA Open Source License. Both these licenses can be +# found in the LICENSE file. + +"""Updates the python binaries that we cache store at +http://storage.google.com/webassembly. + +We only supply binaries for windows and macOS, but we do it very different ways for those two OSes. + +Windows recipe: + 1. Download the "embeddable zip file" version of python from python.org + 2. Remove .pth file to work around https://bugs.python.org/issue34841 + 3. Download and install pywin32 in the `site-packages` directory + 4. Re-zip and upload to storage.google.com + +macOS recipe: + 1. Clone cpython + 2. Use homebrew to install and configure openssl (for static linking!) + 3. Build cpython from source and use `make install` to create archive. +""" + +import glob +import multiprocessing +import os +import platform +import urllib.request +import shutil +import subprocess +import sys +from subprocess import check_call + +version = '3.9.2' +major_minor_version = '.'.join(version.split('.')[:2]) # e.g. '3.9.2' -> '3.9' +base = 'https://www.python.org/ftp/python/%s/' % version +revision = '1' + +pywin32_version = '227' +pywin32_base = 'https://github.com/mhammond/pywin32/releases/download/b%s/' % pywin32_version + +upload_base = 'gs://webassembly/emscripten-releases-builds/deps/' + + +def unzip_cmd(): + # Use 7-Zip if available (https://www.7-zip.org/) + sevenzip = os.path.join(os.getenv('ProgramFiles'), '7-Zip', '7z.exe') + if os.path.isfile(sevenzip): + return [sevenzip, 'x'] + # Fall back to 'unzip' tool + return ['unzip', '-q'] + + +def zip_cmd(): + # Use 7-Zip if available (https://www.7-zip.org/) + sevenzip = os.path.join(os.getenv('ProgramFiles'), '7-Zip', '7z.exe') + if os.path.isfile(sevenzip): + return [sevenzip, 'a', '-mx9'] + # Fall back to 'zip' tool + return ['zip', '-rq'] + + +def make_python_patch(arch): + if arch == 'amd64': + pywin32_filename = 'pywin32-%s.win-%s-py%s.exe' % (pywin32_version, arch, major_minor_version) + else: + pywin32_filename = 'pywin32-%s.%s-py%s.exe' % (pywin32_version, arch, major_minor_version) + filename = 'python-%s-embed-%s.zip' % (version, arch) + out_filename = 'python-%s-%s-embed-%s+pywin32.zip' % (version, revision, arch) + if not os.path.exists(pywin32_filename): + download_url = pywin32_base + pywin32_filename + print('Downloading pywin32: ' + download_url) + urllib.request.urlretrieve(download_url, pywin32_filename) + + if not os.path.exists(filename): + download_url = base + filename + print('Downloading python: ' + download_url) + urllib.request.urlretrieve(download_url, filename) + + os.mkdir('python-embed') + check_call(unzip_cmd() + [os.path.abspath(filename)], cwd='python-embed') + os.remove(os.path.join('python-embed', 'python%s._pth' % major_minor_version.replace('.', ''))) + + os.mkdir('pywin32') + rtn = subprocess.call(unzip_cmd() + [os.path.abspath(pywin32_filename)], cwd='pywin32') + assert rtn in [0, 1] + + os.mkdir(os.path.join('python-embed', 'lib')) + shutil.move(os.path.join('pywin32', 'PLATLIB'), os.path.join('python-embed', 'lib', 'site-packages')) + + check_call(zip_cmd() + [os.path.join('..', out_filename), '.'], cwd='python-embed') + + # cleanup if everything went fine + shutil.rmtree('python-embed') + shutil.rmtree('pywin32') + + upload_url = upload_base + out_filename + print('Uploading: ' + upload_url) + cmd = ['gsutil', 'cp', '-n', out_filename, upload_url] + print(' '.join(cmd)) + check_call(cmd) + + +def build_python(): + if sys.platform.startswith('darwin'): + osname = 'macos' + # Take some rather drastic steps to link openssl statically + check_call(['brew', 'install', 'openssl', 'pkg-config']) + if platform.machine() == 'x86_64': + prefix = '/usr/local' + min_macos_version = '10.13' + elif platform.machine() == 'arm64': + prefix = '/opt/homebrew' + min_macos_version = '11.0' + + osname += '-' + platform.machine() # Append '-x86_64' or '-arm64' depending on current arch. (TODO: Do this for Linux too, move this below?) + + try: + os.remove(os.path.join(prefix, 'opt', 'openssl', 'lib', 'libssl.dylib')) + os.remove(os.path.join(prefix, 'opt', 'openssl', 'lib', 'libcrypto.dylib')) + except Exception: + pass + os.environ['PKG_CONFIG_PATH'] = os.path.join(prefix, 'opt', 'openssl', 'lib', 'pkgconfig') + else: + osname = 'linux' + + src_dir = 'cpython' + if not os.path.exists(src_dir): + check_call(['git', 'clone', 'https://github.com/python/cpython']) + check_call(['git', 'checkout', 'v' + version], cwd=src_dir) + + min_macos_version_line = '-mmacosx-version-min=' + min_macos_version # Specify the min OS version we want the build to work on + build_flags = min_macos_version_line + ' -Werror=partial-availability' # Build against latest SDK, but issue an error if using any API that would not work on the min OS version + env = os.environ.copy() + env['MACOSX_DEPLOYMENT_TARGET'] = min_macos_version + check_call(['./configure', 'CFLAGS=' + build_flags, 'CXXFLAGS=' + build_flags, 'LDFLAGS=' + min_macos_version_line], cwd=src_dir, env=env) + check_call(['make', '-j', str(multiprocessing.cpu_count())], cwd=src_dir, env=env) + check_call(['make', 'install', 'DESTDIR=install'], cwd=src_dir, env=env) + + install_dir = os.path.join(src_dir, 'install') + + # Install requests module. This is needed in particualr on macOS to ensure + # SSL certificates are available (certifi in installed and used by requests). + pybin = os.path.join(src_dir, 'install', 'usr', 'local', 'bin', 'python3') + pip = os.path.join(src_dir, 'install', 'usr', 'local', 'bin', 'pip3') + check_call([pybin, pip, 'install', 'requests']) + + dirname = 'python-%s-%s' % (version, revision) + if os.path.isdir(dirname): + print('Erasing old build directory ' + dirname) + shutil.rmtree(dirname) + os.rename(os.path.join(install_dir, 'usr', 'local'), dirname) + tarball = 'python-%s-%s-%s.tar.gz' % (version, revision, osname) + shutil.rmtree(os.path.join(dirname, 'lib', 'python' + major_minor_version, 'test')) + shutil.rmtree(os.path.join(dirname, 'include')) + for lib in glob.glob(os.path.join(dirname, 'lib', 'lib*.a')): + os.remove(lib) + check_call(['tar', 'zcvf', tarball, dirname]) + print('Uploading: ' + upload_base + tarball) + check_call(['gsutil', 'cp', '-n', tarball, upload_base + tarball]) + + +def main(): + if sys.platform.startswith('win'): + for arch in ('amd64', 'win32'): + make_python_patch(arch) + else: + build_python() + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/test/test.bat b/test/test.bat new file mode 100755 index 0000000000..9fea0c1f43 --- /dev/null +++ b/test/test.bat @@ -0,0 +1,7 @@ +:: equivilent of test.sh as windows bat file +set PATH=%PATH%;%PYTHON_BIN% +CALL emsdk install latest +CALL emsdk activate latest +CALL emsdk_env.bat +CALL python -c "import sys; print(sys.executable)" +CALL emcc.bat -v diff --git a/test/test.py b/test/test.py new file mode 100755 index 0000000000..2824efc66e --- /dev/null +++ b/test/test.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 +import json +import os +import shutil +import subprocess +import sys +import tempfile +import unittest + +WINDOWS = sys.platform.startswith('win') +MACOS = sys.platform == 'darwin' + +assert 'EM_CONFIG' in os.environ, "emsdk should be activated before running this script" + +emconfig = os.environ['EM_CONFIG'] +upstream_emcc = os.path.join('upstream', 'emscripten', 'emcc') +fastcomp_emcc = os.path.join('fastcomp', 'emscripten', 'emcc') +emsdk = './emsdk' +if WINDOWS: + upstream_emcc += '.bat' + fastcomp_emcc += '.bat' + emsdk = 'emsdk.bat' +else: + emsdk = './emsdk' + +# Utilities + + +def listify(x): + if type(x) == list or type(x) == tuple: + return x + return [x] + + +def check_call(cmd, **args): + if type(cmd) != list: + cmd = cmd.split() + print('running: %s' % cmd) + args['universal_newlines'] = True + subprocess.check_call(cmd, **args) + + +def checked_call_with_output(cmd, expected=None, unexpected=None, stderr=None): + cmd = cmd.split(' ') + print('running: %s' % cmd) + try: + stdout = subprocess.check_output(cmd, stderr=stderr, universal_newlines=True) + except subprocess.CalledProcessError as e: + print(e.stderr) + print(e.stdout) + raise e + + if expected: + for x in listify(expected): + assert x in stdout, 'call had the right output: ' + stdout + '\n[[[' + x + ']]]' + if unexpected: + for x in listify(unexpected): + assert x not in stdout, 'call had the wrong output: ' + stdout + '\n[[[' + x + ']]]' + + +def failing_call_with_output(cmd, expected): + proc = subprocess.Popen(cmd.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + stdout, stderr = proc.communicate() + if WINDOWS: + print('warning: skipping part of failing_call_with_output() due to error codes not being propagated (see #592)') + else: + assert proc.returncode, 'call must have failed: ' + str([stdout, "\n========\n", stderr]) + assert expected in stdout or expected in stderr, 'call did not have the right output' + + +def hack_emsdk(marker, replacement): + with open('emsdk.py') as f: + src = f.read() + assert marker in src + src = src.replace(marker, replacement) + name = '__test_emsdk' + with open(name, 'w') as f: + f.write(src) + return name + + +# Set up + +TAGS = json.loads(open('emscripten-releases-tags.txt').read()) + +# Tests + + +def do_lib_building(emcc): + cache_building_messages = ['generating system library: '] + + def do_build(args, expected): + if expected: + expected = cache_building_messages + unexpected = [] + else: + expected = [] + unexpected = cache_building_messages + checked_call_with_output(emcc + ' hello_world.c' + args, + expected=expected, + unexpected=unexpected, + stderr=subprocess.STDOUT) + + # The emsdk ships all system libraries so we don't expect to see any + # cache population unless we explicly --clear-cache. + do_build('', expected=False) + check_call(emcc + ' --clear-cache') + do_build(' -O2', expected=True) + do_build(' -s WASM=0', expected=False) + do_build(' -O2 -s WASM=0', expected=False) + + +def run_emsdk(cmd): + if type(cmd) != list: + cmd = cmd.split() + check_call([emsdk] + cmd) + + +class Emsdk(unittest.TestCase): + @classmethod + def setUpClass(cls): + with open('hello_world.c', 'w') as f: + f.write('''\ +#include + +int main() { + printf("Hello, world!\\n"); + return 0; +} +''') + + def setUp(self): + run_emsdk('install latest') + run_emsdk('activate latest') + + def test_already_installed(self): + # Test we don't re-download unnecessarily + checked_call_with_output(emsdk + ' install latest', expected='already installed', unexpected='Downloading:') + + def test_list(self): + # Test we report installed tools properly. The latest version should be + # installed, but not some random old one. + checked_call_with_output(emsdk + ' list', expected=TAGS['latest'] + ' INSTALLED', unexpected='1.39.15 INSTALLED:') + + def test_config_contents(self): + print('test .emscripten contents') + with open(emconfig) as f: + config = f.read() + assert 'fastcomp' not in config + assert 'upstream' in config + + def test_lib_building(self): + print('building proper system libraries') + do_lib_building(upstream_emcc) + + def test_fastcomp(self): + print('test the last fastcomp release') + run_emsdk('install 1.40.1-fastcomp') + run_emsdk('activate 1.40.1-fastcomp') + + do_lib_building(fastcomp_emcc) + with open(emconfig) as f: + config = f.read() + assert config.count('LLVM_ROOT') == 1 + assert 'upstream' not in config + assert 'fastcomp' in config + + print('verify latest fastcomp version is fixed at 1.40.1') + checked_call_with_output(fastcomp_emcc + ' -v', '1.40.1', stderr=subprocess.STDOUT) + + def test_fastcomp_missing(self): + print('verify that attempting to use newer fastcomp gives an error') + fastcomp_error = 'The fastcomp backend is not getting new builds or releases. Please use the upstream llvm backend or use an older version than 2.0.0 (such as 1.40.1).' + failing_call_with_output(emsdk + ' install latest-fastcomp', fastcomp_error) + failing_call_with_output(emsdk + ' install tot-fastcomp', fastcomp_error) + failing_call_with_output(emsdk + ' install 2.0.0-fastcomp', fastcomp_error) + + def test_redownload(self): + print('go back to using upstream') + run_emsdk('activate latest') + + # Test the normal tools like node don't re-download on re-install + print('another install must re-download') + checked_call_with_output(emsdk + ' uninstall node-14.15.5-64bit') + checked_call_with_output(emsdk + ' install node-14.15.5-64bit', expected='Downloading:', unexpected='already installed') + checked_call_with_output(emsdk + ' install node-14.15.5-64bit', unexpected='Downloading:', expected='already installed') + + def test_tot_upstream(self): + print('test update-tags') + run_emsdk('update-tags') + print('test tot-upstream') + run_emsdk('install tot-upstream') + with open(emconfig) as f: + config = f.read() + run_emsdk('activate tot-upstream') + with open(emconfig + '.old') as f: + old_config = f.read() + self.assertEqual(config, old_config) + # TODO; test on latest as well + check_call(upstream_emcc + ' hello_world.c') + + def test_closure(self): + # Specificlly test with `--closure` so we know that node_modules is working + check_call(upstream_emcc + ' hello_world.c --closure=1') + + def test_specific_old(self): + print('test specific release (old, using sdk-* notation)') + run_emsdk('install sdk-fastcomp-1.38.31-64bit') + run_emsdk('activate sdk-fastcomp-1.38.31-64bit') + + def test_specific_version(self): + print('test specific release (new, short name)') + run_emsdk('install 1.38.33') + print('another install, but no need for re-download') + checked_call_with_output(emsdk + ' install 1.38.33', expected='Skipped', unexpected='Downloading:') + run_emsdk('activate 1.38.33') + with open(emconfig) as f: + config = f.read() + assert 'upstream' not in config + assert 'fastcomp' in config + + def test_specific_version_full(self): + print('test specific release (new, full name)') + run_emsdk('install sdk-1.38.33-upstream-64bit') + run_emsdk('activate sdk-1.38.33-upstream-64bit') + print('test specific release (new, tag name)') + run_emsdk('install sdk-tag-1.38.33-64bit') + run_emsdk('activate sdk-tag-1.38.33-64bit') + + def test_binaryen_from_source(self): + print('test binaryen source build') + run_emsdk(['install', '--build=Release', '--generator=Unix Makefiles', 'binaryen-main-64bit']) + + def test_no_32bit(self): + print('test 32-bit error') + emsdk_hacked = hack_emsdk('not is_os_64bit()', 'True') + failing_call_with_output('python %s install latest' % emsdk_hacked, 'this tool is only provided for 64-bit OSes') + os.remove(emsdk_hacked) + + def test_update_no_git(self): + print('test non-git update') + + temp_dir = tempfile.mkdtemp() + for filename in os.listdir('.'): + if not filename.startswith('.') and not os.path.isdir(filename): + shutil.copy2(filename, os.path.join(temp_dir, filename)) + + os.chdir(temp_dir) + run_emsdk('update') + + print('second time') + run_emsdk('update') + + def test_install_arbitrary(self): + # Test that its possible to install arbrary emscripten-releases SDKs + run_emsdk('install 5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2') + + # Check that its not re-downloaded + checked_call_with_output(emsdk + ' install 5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2', expected='Skipped', unexpected='Downloading:') + + +if __name__ == '__main__': + unittest.main(verbosity=2) diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 0000000000..9d8171e79b --- /dev/null +++ b/test/test.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +echo "test the standard workflow (as close as possible to how a user would do it, in the shell)" + +set -x +set -e + +# Test that arbitrary (non-released) versions can be installed and +# activated. +./emsdk install sdk-upstream-5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2 +./emsdk activate sdk-upstream-5c776e6a91c0cb8edafca16a652ee1ee48f4f6d2 +source ./emsdk_env.sh +which emcc +emcc -v + +# Install an older version of the SDK that requires EM_CACHE to be +# set in the environment, so that we can test it is later removed +./emsdk install sdk-fastcomp-3b8cff670e9233a6623563add831647e8689a86b +./emsdk activate sdk-fastcomp-3b8cff670e9233a6623563add831647e8689a86b +source ./emsdk_env.sh +which emcc +emcc -v +test -n "$EM_CACHE" + +# Install the latest version of the SDK which is the expected precondition +# of test.py. +./emsdk install latest +./emsdk activate latest +source ./emsdk_env.sh --build=Release +# Test that EM_CACHE was unset +test -z "$EM_CACHE" + +# On mac and windows python3 should be in the path and point to the +# bundled version. +which python3 +which emcc +emcc -v diff --git a/test/test_activation.ps1 b/test/test_activation.ps1 new file mode 100644 index 0000000000..1aa0a97c9b --- /dev/null +++ b/test/test_activation.ps1 @@ -0,0 +1,107 @@ +# This test installs emsdk and activates the latest toolchain using `--system` or `--permanent` flags, +# and checks if the environment variables and PATH are correctly updated. Set $env:SYSTEM_FLAG and $env:PERMANENT_FLAG to test each. +# If no flag is provided the process/shell values are tested. See the CI file for an example. + +refreshenv + +$repo_root = [System.IO.Path]::GetDirectoryName((resolve-path "$PSScriptRoot")) + +$PATH_USER_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "User") +$PATH_MACHINE_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") +$PATH_Process_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "Process") + + +try { + + & "$repo_root/emsdk.ps1" install latest + + $esc = '--%' + & "$repo_root/emsdk.ps1" activate latest $esc $env:PERMANENT_FLAG $env:SYSTEM_FLAG + + if ($env:SYSTEM_FLAG) { + $env_type = "Machine" + } + elseif ($env:PERMANENT_FLAG) { + $env_type = "User" + } else { + $env_type = "Process" + } + + $EMSDK = [System.Environment]::GetEnvironmentVariable("EMSDK", $env_type) + $EM_CONFIG = [System.Environment]::GetEnvironmentVariable("EM_CONFIG", $env_type) + $EMSDK_NODE = [System.Environment]::GetEnvironmentVariable("EMSDK_NODE", $env_type) + $EMSDK_PYTHON = [System.Environment]::GetEnvironmentVariable("EMSDK_PYTHON", $env_type) + $JAVA_HOME = [System.Environment]::GetEnvironmentVariable("JAVA_HOME", $env_type) + $PATH = [System.Environment]::GetEnvironmentVariable("PATH", $env_type) + + if (!$EMSDK) { + throw "EMSDK is not set for the user" + } + if (!$EM_CONFIG) { + throw "EM_CONFIG is not set for the user" + } + if (!$EMSDK_NODE) { + throw "EMSDK_NODE is not set for the user" + } + if (!$JAVA_HOME) { + throw "JAVA_HOME is not set for the user" + } + if (!$EMSDK_PYTHON) { + throw "EMSDK_PYTHON is not set for the user" + } + + + $path_split = $PATH.Split(';') + + $EMSDK_Path = $path_split | Where-Object { $_ -like "$repo_root*" } + if (!$EMSDK_Path) { + throw "No path is added!" + } + $EMSDK_NODE_Path = $path_split | Where-Object { $_ -like "$repo_root\node*" } + if (!$EMSDK_NODE_Path) { + throw "$repo_root\\node is not added to path." + } + + $EMSDK_UPSTREAM_Path = $path_split | Where-Object { $_ -like "$repo_root\upstream\emscripten*" } + if (!$EMSDK_UPSTREAM_Path) { + throw "$repo_root\\upstream\emscripten is not added to path." + } + + +} +finally { + # Recover pre-split PATH + refreshenv + + [Environment]::SetEnvironmentVariable("Path", $PATH_USER_BEFORE, "User") + try { + [Environment]::SetEnvironmentVariable("Path", $PATH_MACHINE_BEFORE, "Machine") + } + catch {} + + [Environment]::SetEnvironmentVariable("Path", $PATH_Process_BEFORE, "Process") + + # Recover pre activation env variables + [Environment]::SetEnvironmentVariable("EMSDK", $null, "User") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "User") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "User") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "User") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "User") + + try { + [Environment]::SetEnvironmentVariable("EMSDK", $null, "Machine") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "Machine") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "Machine") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "Machine") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "Machine") + } catch {} + + + [Environment]::SetEnvironmentVariable("EMSDK", $null, "Process") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "Process") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "Process") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "Process") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "Process") + + refreshenv +} diff --git a/test/test_bazel.sh b/test/test_bazel.sh new file mode 100755 index 0000000000..6ae69f9ba8 --- /dev/null +++ b/test/test_bazel.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +echo "test bazel" + +set -x +set -e + +# Get the latest version number from emscripten-releases-tag.txt. +VER=$(grep -oP '(?<=latest\": \")([\d\.]+)(?=\")' \ + emscripten-releases-tags.txt \ + | sed --expression "s/\./\\\./g") +# Based on the latest version number, get the commit hash for that version. +HASH=$(grep "${VER}" emscripten-releases-tags.txt \ + | grep -v latest \ + | cut -f4 -d\") + +FAILMSG="!!! scripts/update_bazel_toolchain.sh needs to be run !!!" + +# Ensure the WORKSPACE file is up to date with the latest version. +grep ${VER} bazel/revisions.bzl || (echo ${FAILMSG} && false) +grep ${HASH} bazel/revisions.bzl || (echo ${FAILMSG} && false) + +cd bazel +bazel build //hello-world:hello-world-wasm +bazel build //hello-world:hello-world-wasm-simd + +cd test_external +bazel build //:hello-world-wasm diff --git a/test/test_bazel_mac.sh b/test/test_bazel_mac.sh new file mode 100755 index 0000000000..851ec5fdde --- /dev/null +++ b/test/test_bazel_mac.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +echo "test bazel" + +set -x +set -e + +# Get the latest version number from emscripten-releases-tag.txt. +VER=$(ggrep -oP '(?<=latest\": \")([\d\.]+)(?=\")' \ + emscripten-releases-tags.txt \ + | sed "s/\./\\\./g") +# Based on the latest version number, get the commit hash for that version. +HASH=$(grep "${VER}" emscripten-releases-tags.txt \ + | grep -v latest \ + | cut -f4 -d\") + +FAILMSG="!!! scripts/update_bazel_toolchain.sh needs to be run !!!" + +# Ensure the WORKSPACE file is up to date with the latest version. +grep ${VER} bazel/revisions.bzl || (echo ${FAILMSG} && false) +grep ${HASH} bazel/revisions.bzl || (echo ${FAILMSG} && false) + +cd bazel +bazel build //hello-world:hello-world-wasm +bazel build //hello-world:hello-world-wasm-simd + +cd test_external +bazel build //:hello-world-wasm diff --git a/test/test_path_preservation.ps1 b/test/test_path_preservation.ps1 new file mode 100644 index 0000000000..b894954564 --- /dev/null +++ b/test/test_path_preservation.ps1 @@ -0,0 +1,148 @@ +# This test installs emsdk and activates the latest toolchain using `--system` or `--permanent` flags, +# and checks if parts of PATH are lost or overwritten. Set $env:SYSTEM_FLAG and $env:PERMANENT_FLAG to test each. +# If no flag is provided the process/shell values are tested. See the CI file for an example. + +refreshenv + +$repo_root = [System.IO.Path]::GetDirectoryName((resolve-path "$PSScriptRoot")) + +$PATH_USER_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "User") +$PATH_MACHINE_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") +$PATH_Process_BEFORE = [System.Environment]::GetEnvironmentVariable("PATH", "Process") + +try { + + + & "$repo_root/emsdk.ps1" install latest + + $esc = '--%' + & "$repo_root/emsdk.ps1" activate latest $esc $env:PERMANENT_FLAG $env:SYSTEM_FLAG + + $PATH_USER = [System.Environment]::GetEnvironmentVariable("PATH", "User") + $PATH_MACHINE = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + $PATH_Process = [System.Environment]::GetEnvironmentVariable("PATH", "Process") + + if ($env:SYSTEM_FLAG) { + echo "--system test............................." + $path_before_arr = $PATH_MACHINE_BEFORE.Split(';') + $path_arr = $PATH_MACHINE.Split(';') + } + elseif ($env:PERMANENT_FLAG) { + echo "--permanent test.........................." + $path_before_arr = $PATH_USER_BEFORE.Split(';') + $path_arr = $PATH_USER.Split(';') + } else { + echo "no flag test (shell/process).............." + $path_before_arr = $PATH_Process_BEFORE.Split(';') + $path_arr = $PATH_Process.Split(';') + } + + + $EMSDK_Path = $path_arr | Where-Object { $_ -like "$repo_root*" } + $EMSDK_NODE_Path = $path_arr | Where-Object { $_ -like "$repo_root\node*" } + $EMSDK_PYTHON_Path = $path_arr | Where-Object { $_ -like "$repo_root\python*" } + $EMSDK_JAVA_Path = $path_arr | Where-Object { $_ -like "$repo_root\java*" } + $EMSDK_UPSTREAM_Path = $path_arr | Where-Object { $_ -like "$repo_root\upstream\emscripten*" } + + $number_of_items = $path_arr.count + [System.Collections.ArrayList]$rest_of_path = @() + Foreach ($item in $path_arr) { + if ( + ($item -like "$repo_root*") -or + ($item -like "$repo_root\node*") -or + ($item -like "$repo_root\python*") -or + ($item -like "$repo_root\java*") -or + ($item -like "$repo_root\upstream\emscripten*") + ) { + echo "$item is on the PATH" + } + else { + $rest_of_path.add($item) | Out-Null + } + } + + # compare the PATHs before activation and after activation + if (Compare-Object -ReferenceObject $path_before_arr -DifferenceObject $rest_of_path ) { + echo "Old path is ............................." + echo $path_before_arr + echo "Current rest of path is ................." + echo $rest_of_path + throw "some parts of PATH are removed" + } + + # Compare the other untouched PATH + if ($env:SYSTEM_FLAG) { + if (Compare-Object -ReferenceObject $PATH_USER_BEFORE.Split(';') -DifferenceObject $PATH_USER.Split(';') ) { + echo "Old user path is ...................." + echo $PATH_USER_BEFORE + echo "Current user path is ................" + echo $PATH_USER + throw "User PATH are changed while --system had been provided" + } + } + elseif ($env:PERMANENT_FLAG) { + if (Compare-Object -ReferenceObject $PATH_MACHINE_BEFORE.Split(';') -DifferenceObject $PATH_MACHINE.Split(';') ) { + echo "Old machine path is.................." + echo $PATH_MACHINE_BEFORE + echo "Current machine path is.............." + echo $PATH_MACHINE + throw "MACHINE PATH are changed while --system was not provided" + } + } else { + if ( + (Compare-Object -ReferenceObject $PATH_MACHINE_BEFORE.Split(';') -DifferenceObject $PATH_MACHINE.Split(';')) -or + (Compare-Object -ReferenceObject $PATH_MACHINE_BEFORE.Split(';') -DifferenceObject $PATH_MACHINE.Split(';')) + ) { + echo "Old machine path is.................." + echo $PATH_MACHINE_BEFORE + echo "Current machine path is.............." + echo $PATH_MACHINE + echo "Old user path is ...................." + echo $PATH_USER_BEFORE + echo "Current user path is ................" + echo $PATH_USER + throw "MACHINE/USER PATH are changed while no flag was provided" + } + } + + + + +} +finally { + # Recover pre-split PATH + refreshenv + + [Environment]::SetEnvironmentVariable("Path", $PATH_USER_BEFORE, "User") + try { + [Environment]::SetEnvironmentVariable("Path", $PATH_MACHINE_BEFORE, "Machine") + } + catch {} + + [Environment]::SetEnvironmentVariable("Path", $PATH_Process_BEFORE, "Process") + + # Recover pre activation env variables + [Environment]::SetEnvironmentVariable("EMSDK", $null, "User") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "User") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "User") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "User") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "User") + + try { + [Environment]::SetEnvironmentVariable("EMSDK", $null, "Machine") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "Machine") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "Machine") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "Machine") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "Machine") + } catch {} + + + [Environment]::SetEnvironmentVariable("EMSDK", $null, "Process") + [Environment]::SetEnvironmentVariable("EM_CONFIG", $null, "Process") + [Environment]::SetEnvironmentVariable("EMSDK_NODE", $null, "Process") + [Environment]::SetEnvironmentVariable("EMSDK_PYTHON", $null, "Process") + [Environment]::SetEnvironmentVariable("JAVA_HOME", $null, "Process") + + refreshenv + +} diff --git a/test/test_source_env.sh b/test/test_source_env.sh new file mode 100755 index 0000000000..71a3e4070a --- /dev/null +++ b/test/test_source_env.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash + +echo "Test ability to source emsdk_env.sh in different shells" + +if [ -n "$EMSDK" ]; then + echo "EMSDK is already defined in this shell. Run tests in a shell without sourcing emsdk_env.sh first" + exit 1 +fi + +DIR=$(dirname "$BASH_SOURCE") + +# setup a symlink relative to the current dir +REL_LINK_DIR="$DIR/tmp" +if [ -d "$REL_LINK_DIR" ]; then + rm -rf "$REL_LINK_DIR" +fi +echo "Creating links in $REL_LINK_DIR" +mkdir -p "$REL_LINK_DIR" +(cd $DIR/.. && ln -s `pwd` "$REL_LINK_DIR/emsdk") +(cd $DIR/.. && ln -s `pwd`/emsdk_env.sh "$REL_LINK_DIR") + +# setup a symlink in an absolute directory +ABS_LINK_DIR="/tmp/emsdk_env_test" +if [ -d "$ABS_LINK_DIR" ]; then + rm -rf "$ABS_LINK_DIR" +fi +echo "Creating links in $ABS_LINK_DIR" +mkdir -p "$ABS_LINK_DIR" +(cd $DIR/.. && ln -s `pwd` "$ABS_LINK_DIR/emsdk") +(cd $DIR/.. && ln -s `pwd`/emsdk_env.sh "$ABS_LINK_DIR") + +PATH1="$DIR/../emsdk_env.sh" +PATH2="$REL_LINK_DIR/emsdk/emsdk_env.sh" +PATH3="$REL_LINK_DIR/emsdk_env.sh" +PATH4="$ABS_LINK_DIR/emsdk/emsdk_env.sh" +PATH5="$ABS_LINK_DIR/emsdk_env.sh" + +assert_emcc() { + current=$1 + cmd=$2 + value=$3 + if [ -z "$value" ] || [ "$value" == "false" ]; then + echo "FAILED: $current" + echo " unable to get EMSDK in $current using '$cmd'" + else + echo "SUCCESS: $current testing $cmd" + echo " -> EMSDK = $value" + fi +} + +test_bash() { + value=$(bash --rcfile <(echo $1)) + assert_emcc bash "$1" "$value" +} + +test_zsh() { + value=$(zsh -d -c "$1") + assert_emcc zsh "$1" "$value" +} + +test_ksh() { + value=$(ksh -c "$1") + assert_emcc ksh "$1" "$value" +} + +it_tests_direct_path() { + TEST_SCRIPT=". ${PATH1}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" + TEST_SCRIPT="source ${PATH1}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" +} + +it_tests_via_relative_dir_symlink() { + TEST_SCRIPT=". ${PATH2}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" + TEST_SCRIPT="source ${PATH2}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" +} + +it_tests_via_relative_file_symlink() { + TEST_SCRIPT=". ${PATH3}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" + TEST_SCRIPT="source ${PATH3}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" +} + +it_tests_via_absolute_dir_symlink() { + TEST_SCRIPT=". ${PATH4}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" + TEST_SCRIPT="source ${PATH4}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" +} + +it_tests_via_absolute_file_symlink() { + TEST_SCRIPT=". ${PATH5}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" + TEST_SCRIPT="source ${PATH5}"' >/dev/null 2>&1; if [ -n "$EMSDK" ]; then echo "$EMSDK"; else echo false; fi ; exit' + test_bash "$TEST_SCRIPT" + test_zsh "$TEST_SCRIPT" + test_ksh "$TEST_SCRIPT" +} + +run_bash_tests() { + it_tests_direct_path + it_tests_via_relative_dir_symlink + it_tests_via_relative_file_symlink + it_tests_via_absolute_dir_symlink + it_tests_via_absolute_file_symlink +} + +run_bash_tests + +rm -rf $REL_LINK_DIR +rm -rf $ABS_LINK_DIR diff --git a/upstream/lkgr.json b/upstream/lkgr.json deleted file mode 100644 index 8295fdbd75..0000000000 --- a/upstream/lkgr.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "repositories": { - "java": null, - "gcc": { - "remote": "https://chromium.googlesource.com/chromiumos/third_party/gcc.git", - "subject": "[GCC] Fix compile time error introduced by PR64111 (GCC FSF).", - "hash": "b6125c702850488ac3bfb1079ae5c9db89989406", - "name": "Caroline Tice", - "email": "cmtice@google.com" - }, - "llvm": { - "remote": "https://github.com/llvm/llvm-project.git", - "subject": "[COFF] Remove finalizeContents virtual method from Chunk, NFC", - "hash": "11c141eb68531eec30af8ff8f82b8159de99e555", - "name": "Reid Kleckner", - "email": "rnk@google.com" - }, - "emscripten": { - "remote": "https://github.com/emscripten-core/emscripten.git", - "subject": "Add assertions for duplicate names from parameterization (#8665)", - "hash": "f6df93be62459edaecf38e3986a96118be8ace94", - "name": "Guanzhong Chen", - "email": "gzchen@google.com" - }, - "nodejs": null, - "llvm-test-suite": { - "remote": "https://llvm.googlesource.com/test-suite.git", - "subject": "Document how to use the 'vs' mode in compare.py", - "hash": "c79e9b9f75512e75c3241b08ec78f78d8fc710af", - "name": "Amara Emerson", - "email": "aemerson@apple.com" - }, - "host-toolchain": { - "remote": "https://chromium.googlesource.com/v8/v8.git", - "subject": "Revert \"Reland \"[torque] move class tests to unittests\"\"", - "hash": "0ef1982ff57d1b5db66d4e4354193f4aee543d13", - "name": "Francis McCabe", - "email": "fgm@chromium.org" - }, - "waterfall": { - "remote": "/b/swarming/w/ir/cache/git/chromium.googlesource.com-external-github.com-webassembly-waterfall", - "subject": "Print reason for clobbering build (#528)", - "hash": "02d964670cedc2e43f38151e8fbbc69a167187d8", - "name": "Derek Schuff", - "email": "dschuff@chromium.org" - }, - "fastcomp-clang": { - "remote": "https://github.com/emscripten-core/emscripten-fastcomp-clang.git", - "subject": "1.38.32", - "hash": "ca75f5e8a424747b1e368ad6e94a4b4740dd28af", - "name": "Alon Zakai", - "email": "azakai@google.com" - }, - "binaryen": { - "remote": "https://chromium.googlesource.com/external/github.com/WebAssembly/binaryen.git", - "subject": "Inlining: exposed inlining thresholds as command-line parameters. (#2125)", - "hash": "5644d15b87577478659d7cbeb9bb0555cc233631", - "name": "Wouter van Oortmerssen", - "email": "aardappel@gmail.com" - }, - "fastcomp": { - "remote": "https://github.com/emscripten-core/emscripten-fastcomp.git", - "subject": "1.38.32", - "hash": "2be857f52bb377de8cf7369acfc42a3b36bbd94d", - "name": "Alon Zakai", - "email": "azakai@google.com" - }, - "v8": { - "remote": "https://chromium.googlesource.com/v8/v8.git", - "subject": "Revert \"Reland \"[torque] move class tests to unittests\"\"", - "hash": "0ef1982ff57d1b5db66d4e4354193f4aee543d13", - "name": "Francis McCabe", - "email": "fgm@chromium.org" - }, - "cmake": null, - "wabt": { - "remote": "https://chromium.googlesource.com/external/github.com/WebAssembly/wabt.git", - "subject": "[wasm-objdump] Fix crash on invalid reloc sections (#1084)", - "hash": "a3a7a93f1f417c6c258e6b5d507adb004fc8b688", - "name": "Sam Clegg", - "email": "sbc@chromium.org" - }, - "wasi-sysroot": { - "remote": "https://github.com/CraneStation/wasi-sysroot.git", - "subject": "Don't declare popen and pclose.", - "hash": "eee6ee7566e26f2535eb6088c8494a112ff423b9", - "name": "Dan Gohman", - "email": "sunfish@mozilla.com" - }, - "gnuwin32": null - }, - "build": "6480", - "scheduler": null -}