Rust, Windows, Travis CI…

Travis recently announced early stage Windows support, and it already supports Rust. I couldn’t wait to try it—I don’t really enjoy AppVeyor—so I set it up for mdcat and it worked really well.

The change was really simple, and I just took me a few iterations to get almost everything—in particular deployments to Github releases—working just like on Linux and macOS. I could eventually remove AppVeyor entirely which makes me really happy. This post describes the change and also outlines what little’s still missing for me.

The change

The following diff contains the entire change:

@@ -76,6 +77,15 @@ jobs:
         - export PATH="$HOME/Library/Python/2.7/bin:$PATH"
       env:
         - CARGOTARGET=x86_64-apple-darwin
+    - stage: test
+      os: windows
+      rust: stable
+      # Don't install ansi2html on Travis CI: Python isn’t readily available on
+      # Windows, so skip these tests
+      before_install: ''
+      env:
+        - CARGOTARGET=x86_64-pc-windows-msvc
+        - CARGOFLAGS='--no-default-features'
     # Catch regressions in beta and nightly
     - stage: test
       os: linux
@@ -116,20 +126,23 @@ jobs:
       install: ""
       script: cargo build --target "$CARGOTARGET" $CARGOFLAGS --release --verbose
       before_deploy:
+        # Strip the binary to reduce its size
+        - strip "target/${CARGOTARGET}/release/mdcat"
+        # Put all the things into a dedicated directory, as usual with TAR files
         - export ARCHIVE_NAME="${TRAVIS_TAG}-${CARGOTARGET}"
         - echo "Building ${ARCHIVE_NAME}"
         - mkdir "/tmp/${ARCHIVE_NAME}"
         - cp README.md CHANGELOG.md LICENSE "/tmp/${ARCHIVE_NAME}"
         - cp "target/${CARGOTARGET}/release/mdcat" "/tmp/${ARCHIVE_NAME}"
-        # Strip the binary to reduce its size
-        - strip "/tmp/${ARCHIVE_NAME}/mdcat"
-        - tar -czf "/tmp/${ARCHIVE_NAME}.tar.gz" -C /tmp/ "${ARCHIVE_NAME}"
+        # Pack the tarball
+        - export ARCHIVE_FILE="/tmp/${ARCHIVE_NAME}.tar.gz"
+        - tar -czf "${ARCHIVE_FILE}" -C /tmp/ "${ARCHIVE_NAME}"
       deploy:
         skip_cleanup: true
         provider: releases
         api_key:
           secure: [elided]
-        file: /tmp/${ARCHIVE_NAME}.tar.gz
+        file: "${ARCHIVE_FILE}"
         on:
           tags: true
     - <<: *deploy-settings
@@ -140,3 +153,17 @@ jobs:
         # Only include iterm2 when building for macos, and also enable remote
         # resources for iterm2
         - CARGOFLAGS='--no-default-features --features iterm2,remote_resources'
+    - <<: *deploy-settings
+      os: windows
+      before_install: ""
+      env:
+        - CARGOTARGET=x86_64-pc-windows-msvc
+        - CARGOFLAGS='--no-default-features'
+      before_deploy:
+        # Windows likes file extensions :)
+        - mv LICENSE LICENSE.txt
+        # and ZIP files
+        - export ARCHIVE_FILE="${TRAVIS_TAG}-${CARGOTARGET}.zip"
+        # The leading dots are important: They make 7z ignore the path part and
+        # add the file directly to the archive root.
+        - 7z a "${ARCHIVE_FILE}" "./target/${CARGOTARGET}/release/mdcat.exe" ./README.md ./CHANGELOG.md ./LICENSE.txt

I find that this is really surprisingly litte given that I really setup a totally alien system here. After all, not being based on Unix Windows is really very different from macOS and Linux, yet I can build for both systems with almost the same configuration. That’s really cool.

In particular I didn’t have to change anything in the actual build scripts: The Windows environment runs in Git bash, so I can use the same tools and syntax as for macOS and Linux. No more BAT or powershell scripts like on AppVeyor.

The biggest change is the deployment script, and that’s only because I wanted to pack the Windows binary in a ZIP file which seems a lot more common than TAR files on Windows. Since I could no longer use a hard-coded .tar.gz file extension I also had to add a new $ARCHIVE_FILE variable to tell Travis CI what file to deploy, hence the small changes the the deployment configuration for Linux and macOS above.

I took me two or three iterations on a test branch with some dummy tags, but in the end the new 0.11.0 release has all its binaries built on Travis CI.

What’s missing?

I use a Python utility in some tests for mdcat (see formatting.rs). I couldn’t get Python to work after a three or four attempts and I didn’t want to spend more time on it, so in the end I decided to disable these tests—they’re not system-dependent anyway, so I think that’s an an acceptable price to pay for an overall much simpler Windows CI setup.

I didn’t try any more fancy stuff, like using C libraries, etc—mdcat only needs pure Rust dependencies on Windows—so for more complex projects there are probably a lot more differences between different environments and also to AppVeyor than this post suggests, but for a simple plain Rust project the new Windows environment does a great job.

Thank you Travis CI!