diff --git a/.travis.yml b/.travis.yml index f2b1a1545..c15e351c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,12 @@ matrix: - os: osx osx_image: xcode8.3 rust: stable + # static build + - os: linux + sudo: required + dist: xenial + rust: nightly + env: IMAGE=x86_64-alpine-linux-musl allow_failures: - rust: nightly @@ -80,8 +86,13 @@ before_script: script: - | - cargo build --verbose && - cargo test + if [[ -z "$IMAGE" ]]; then + cargo build --verbose && + cargo test + else + docker build -t ttci-$IMAGE dist/docker/$IMAGE/ && + docker run -v $(pwd):/tectonic ttci-$IMAGE + fi after_success: | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then diff --git a/Cargo.toml b/Cargo.toml index a9c73b9c9..c49b88fdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ crate-type = ["rlib"] [build-dependencies] cc = "^1.0" -pkg-config = "^0.3" +pkg-config = "^0.3" # note: sync dist/docker/*/pkg-config-rs.sh with the version in Cargo.lock regex = "^1.0" sha2 = "^0.8" @@ -51,13 +51,17 @@ libc = "^0.2" tempfile = "^3.0" md-5 = "^0.8" sha2 = "^0.8" -serde = "^1.0" -serde_derive = "^1.0" +serde = { version = "^1.0", optional = true } +serde_derive = { version = "^1.0", optional = true } tectonic_xdv = { path = "xdv", version = "0.1.9-dev" } termcolor = "^1.0" toml = "^0.4" zip = "^0.4" +[features] +default = ["serialization"] +serialization = ["serde", "serde_derive"] + # freetype-sys = "^0.4" # harfbuzz-sys = "^0.1" # libz-sys = "^1.0" diff --git a/README.md b/README.md index efcbb6adc..ac0d8dacb 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,21 @@ any given time. Please see [the tectonic-staging README](https://github.com/tectonic-typesetting/tectonic-staging#readme) for more information. (Or at least, more words on the topic.) + + +## Features + +The Tectonic build can be customized with the following features: + +##### serialization (enabled by default) + +This feature enables (de)serialization using the [serde](https://serde.rs/) +crate. At the moment, this is only used to read per-user configuration from a +[TOML](https://github.com/toml-lang/toml) file. If this feature is disabled, +the per-user configuration file will be silently ignored. + +This functionality is optional because it requires the `serde_derive` crate, +which in turn uses Rust’s `proc_macro` feature. The `proc_macro` functionality +[is not available on musl targets](https://github.com/rust-lang/rust/issues/40174), +and so must be turned off if you wish to build a completely static Tectonic +executable. diff --git a/dist/docker/x86_64-alpine-linux-musl/Dockerfile b/dist/docker/x86_64-alpine-linux-musl/Dockerfile new file mode 100644 index 000000000..038920cc3 --- /dev/null +++ b/dist/docker/x86_64-alpine-linux-musl/Dockerfile @@ -0,0 +1,42 @@ +# Copyright 2018 The Tectonic Project +# Licensed under the MIT License. + +FROM alpine:edge + +RUN apk update && \ + apk add \ + g++ \ + git \ + rust \ + cargo \ + fontconfig-dev \ + freetype-static \ + glib-static \ + graphite2-dev \ + graphite2-static \ + harfbuzz-dev \ + harfbuzz-static \ + icu-dev \ + icu-static \ + openssl-dev \ + zlib-dev + +ADD pkg-config-rs.sh / +RUN /pkg-config-rs.sh + +ENV PKG_CONFIG_ALL_STATIC=1 +ENV OPENSSL_STATIC=1 +ENV OPENSSL_DIR=/usr + +# cc-rs does not support static linking stdc++, +# so we omit linking information on build.rs by setting CXXSTDLIB='' +# and specify static linking in RUSTFLAGS +ENV CXXSTDLIB="" +ENV RUSTFLAGS="-L /usr/lib -l static=stdc++ -C target-feature=+crt-static" + +# Use a patched pkg-config-rs to allow static linking with system libraries. +# The --no-default-features flag removes serde-derive as a dep, which doesn't +# work when linking statically (rust-lang#40147). +CMD cd /tectonic && \ + echo -e "[patch.crates-io]\npkg-config = { path = \"/pkg-config-rs\" }" >> Cargo.toml && \ + cargo test --release --no-default-features diff --git a/dist/docker/x86_64-alpine-linux-musl/pkg-config-rs.sh b/dist/docker/x86_64-alpine-linux-musl/pkg-config-rs.sh new file mode 100755 index 000000000..6edf7b458 --- /dev/null +++ b/dist/docker/x86_64-alpine-linux-musl/pkg-config-rs.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# Copyright 2018 The Tectonic Project +# Licensed under the MIT License. + +set -ex + +git clone --branch 0.3.14 https://github.com/alexcrichton/pkg-config-rs /pkg-config-rs + +# make pkg-config-rs allows static linking with system libraries +cd /pkg-config-rs +patch -p1 <<'EOF' +diff --git a/src/lib.rs b/src/lib.rs +index 88dd310..ffcd7ae 100644 +--- a/src/lib.rs ++++ b/src/lib.rs +@@ -547,7 +547,7 @@ fn is_static_available(name: &str, dirs: &[PathBuf]) -> bool { + }; + + dirs.iter().any(|dir| { +- !system_roots.iter().any(|sys| dir.starts_with(sys)) && ++ // !system_roots.iter().any(|sys| dir.starts_with(sys)) && + dir.join(&libname).exists() + }) + } +EOF diff --git a/src/config.rs b/src/config.rs index d50a865c6..04ed8b798 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,15 +10,12 @@ //! running the command-line client. So we begrudgingly have a *little* //! configuration. -use std::io::{Read, Write}; -use std::io::ErrorKind as IoErrorKind; use std::ffi::OsStr; use std::fs::File; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; -use app_dirs::{app_dir, app_root, get_app_root, sanitized, AppDataType}; -use toml; +use app_dirs::{app_dir, sanitized, AppDataType}; use errors::{ErrorKind, Result}; use io::itarbundle::{HttpITarIoFactory, ITarBundle}; @@ -41,24 +38,32 @@ pub fn activate_config_test_mode(forced: bool) { CONFIG_TEST_MODE_ACTIVATED.store(forced, Ordering::SeqCst); } - -const DEFAULT_CONFIG: &'static str = r#"[[default_bundles]] -url = "https://archive.org/services/purl/net/pkgwpub/tectonic-default" -"#; - - -#[derive(Deserialize)] +#[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] pub struct PersistentConfig { default_bundles: Vec, } -#[derive(Deserialize)] +#[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] pub struct BundleInfo { url: String, } impl PersistentConfig { + #[cfg(feature = "serialization")] + /// Open the per-user configuration file. + /// + /// This file is stored in TOML format. If the configuration file does not + /// exist, no error is signaled — instead, a basic default configuration + /// is returned. In this case, if `auto_create_config_file` is true, the + /// configuration file (and the directory containing it) will be + /// automatically created, filling in the default configuration. If it is + /// false, the default configuration is returned and the filesystem is not + /// modified. pub fn open(auto_create_config_file: bool) -> Result { + use toml; + use std::io::{Read, Write}; + use std::io::ErrorKind as IoErrorKind; + use app_dirs::{app_root, get_app_root}; let mut cfg_path = if auto_create_config_file { app_root(AppDataType::UserConfig, &::APP_INFO)? } else { @@ -75,11 +80,12 @@ impl PersistentConfig { Err(e) => { if e.kind() == IoErrorKind::NotFound { // Config file didn't exist -- that's OK. + let config = PersistentConfig::default(); if auto_create_config_file { let mut f = File::create(&cfg_path)?; - write!(f, "{}", DEFAULT_CONFIG)?; + write!(f, "{}", toml::to_string(&config)?)?; } - toml::from_str(DEFAULT_CONFIG)? + config } else { // Uh oh, unexpected error reading the config file. return Err(e.into()); @@ -90,6 +96,18 @@ impl PersistentConfig { Ok(config) } + #[cfg(not(feature = "serialization"))] + /// Return a default configuration structure. + /// + /// In most builds of Tectonic, this function reads a per-user + /// configuration file and returns it. However, this version of Tectonic + /// has been built without the `serde` feature, so it cannot deserialize + /// the file. Therefore, this function always returns the default + /// configuration. + pub fn open(_auto_create_config_file: bool) -> Result { + Ok(PersistentConfig::default()) + } + pub fn make_cached_url_provider(&self, url: &str, only_cached: bool, status: &mut StatusBackend) -> Result> { let itb = ITarBundle::::new(url); @@ -157,9 +175,14 @@ impl PersistentConfig { } } - impl Default for PersistentConfig { fn default() -> Self { - toml::from_str(DEFAULT_CONFIG).expect("un-parseable built-in default configuration (?!)") + PersistentConfig { + default_bundles: vec![ + BundleInfo { + url: String::from("https://archive.org/services/purl/net/pkgwpub/tectonic-default"), + } + ] + } } } diff --git a/src/errors.rs b/src/errors.rs index 23d32da70..ddf2a1483 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -27,6 +27,7 @@ error_chain! { ParseInt(num::ParseIntError); Persist(tempfile::PersistError); TomlDe(toml::de::Error); + TomlSer(toml::ser::Error); Utf8(str::Utf8Error); Xdv(tectonic_xdv::XdvError); Zip(ZipError); diff --git a/src/lib.rs b/src/lib.rs index 15a54afca..175a74bc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,8 +59,8 @@ extern crate hyper_native_tls; extern crate libc; extern crate md5; extern crate tempfile; -#[macro_use] extern crate serde_derive; -extern crate serde; +#[cfg(feature = "serde_derive")] #[macro_use] extern crate serde_derive; +#[cfg(feature = "serde")] extern crate serde; extern crate sha2; extern crate tectonic_xdv; extern crate termcolor; diff --git a/tectonic/dpx-dpxcrypt.c b/tectonic/dpx-dpxcrypt.c index 1d3e9eb89..cc4d357be 100644 --- a/tectonic/dpx-dpxcrypt.c +++ b/tectonic/dpx-dpxcrypt.c @@ -1228,10 +1228,10 @@ AES_ecb_encrypt (const unsigned char *key, size_t key_len, /* NULL iv means here "use random IV". */ void -AES_cbc_encrypt (const unsigned char *key, size_t key_len, - const unsigned char *iv, int padding, - const unsigned char *plain, size_t plain_len, - unsigned char **cipher, size_t *cipher_len) +AES_cbc_encrypt_tectonic (const unsigned char *key, size_t key_len, + const unsigned char *iv, int padding, + const unsigned char *plain, size_t plain_len, + unsigned char **cipher, size_t *cipher_len) { AES_CONTEXT *ctx, aes; const unsigned char *inptr; diff --git a/tectonic/dpx-dpxcrypt.h b/tectonic/dpx-dpxcrypt.h index 9e3985630..ec3916d5b 100644 --- a/tectonic/dpx-dpxcrypt.h +++ b/tectonic/dpx-dpxcrypt.h @@ -86,9 +86,9 @@ void AES_ecb_encrypt (const unsigned char *key, size_t key_len, const unsigned char *plain, size_t plain_len, unsigned char **cipher, size_t *cipher_len); -void AES_cbc_encrypt (const unsigned char *key, size_t key_len, - const unsigned char *iv, int padding, - const unsigned char *plain, size_t plain_len, - unsigned char **cipher, size_t *cipher_len); +void AES_cbc_encrypt_tectonic (const unsigned char *key, size_t key_len, + const unsigned char *iv, int padding, + const unsigned char *plain, size_t plain_len, + unsigned char **cipher, size_t *cipher_len); #endif /* _DPXCRYPT_H_ */ diff --git a/tectonic/dpx-pdfencrypt.c b/tectonic/dpx-pdfencrypt.c index 2d93ba9a8..65a90debb 100644 --- a/tectonic/dpx-pdfencrypt.c +++ b/tectonic/dpx-pdfencrypt.c @@ -325,7 +325,7 @@ compute_hash_V5 (unsigned char *hash, Kr = NEW(K1_len * 64, unsigned char); for (i = 0; i < 64; i++) memcpy(Kr + i * K1_len, K1, K1_len); - AES_cbc_encrypt(K, 16, K + 16, 0, Kr, K1_len * 64, &E, &E_len); + AES_cbc_encrypt_tectonic(K, 16, K + 16, 0, Kr, K1_len * 64, &E, &E_len); free(Kr); for (i = 0; i < 16; i++) @@ -392,7 +392,7 @@ compute_owner_password_V5 (struct pdf_sec *p, const char *oplain) compute_hash_V5(hash, oplain, ksalt, p->U, p->R); memset(iv, 0, AES_BLOCKSIZE); - AES_cbc_encrypt(hash, 32, iv, 0, p->key, p->key_size, &OE, &OE_len); + AES_cbc_encrypt_tectonic(hash, 32, iv, 0, p->key, p->key_size, &OE, &OE_len); memcpy(p->OE, OE, 32); free(OE); } @@ -417,7 +417,7 @@ compute_user_password_V5 (struct pdf_sec *p, const char *uplain) compute_hash_V5(hash, uplain, ksalt, NULL, p->R); memset(iv, 0, AES_BLOCKSIZE); - AES_cbc_encrypt(hash, 32, iv, 0, p->key, p->key_size, &UE, &UE_len); + AES_cbc_encrypt_tectonic(hash, 32, iv, 0, p->key, p->key_size, &UE, &UE_len); memcpy(p->UE, UE, 32); free(UE); } @@ -634,12 +634,12 @@ pdf_encrypt_data (const unsigned char *plain, size_t plain_len, break; case 4: calculate_key(p, key); - AES_cbc_encrypt(key, MIN(16, p->key_size + 5), NULL, 1, - plain, plain_len, cipher, cipher_len); + AES_cbc_encrypt_tectonic(key, MIN(16, p->key_size + 5), NULL, 1, + plain, plain_len, cipher, cipher_len); break; case 5: - AES_cbc_encrypt(p->key, p->key_size, NULL, 1, - plain, plain_len, cipher, cipher_len); + AES_cbc_encrypt_tectonic(p->key, p->key_size, NULL, 1, + plain, plain_len, cipher, cipher_len); break; default: _tt_abort("pdfencrypt: Unexpected V value: %d", p->V);