Skip to content

Commit c8aafad

Browse files
authored
Alpine (#36)
* easy static builds for hts-nim projects * curl almost working * more curl stuff * try for static build on alpine * static builds with musl * update gitignore
1 parent 44dc679 commit c8aafad

File tree

8 files changed

+352
-9
lines changed

8 files changed

+352
-9
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ hts/hts
44
tests/htstest
55
tests/vcftest
66
tests/bgzftest
7+
tests/bamtest
8+
tests/all
9+
tests/run
10+
docker/nsb
11+
docker/hts_nim_static_builder
12+

Dockerfile

Lines changed: 110 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,113 @@
1-
FROM ubuntu:16.04
1+
## this docker file is based on a relatively old setup so that libc dependencies
2+
## should not be a problem. It:
3+
# 1. builds htslib and all dependencies currently without libcurl
4+
# 2. installs nim
5+
# 3. sets up a nim binary (nsb) that is expected to be called from an external binary (static_builder)
6+
# These facilitate building static binaries for projects using hts-nim.
27

3-
RUN apt-get update \
4-
&& apt-get -qy install curl libssl-dev build-essential gcc \
5-
&& curl -sSfLo init.sh https://nim-lang.org/choosenim/init.sh \
6-
&& bash init.sh -y \
7-
&& rm init.sh \
8-
&& echo "export PATH=/root/.nimble/bin:$PATH" >> /etc/profile \
9-
&& echo "export PATH=/root/.nimble/bin:$PATH" >> /etc/bash.bashrc \
8+
# docker build -t brentp/hts-nim:latest -f Dockerfile .
9+
FROM centos:centos6
1010

11+
RUN yum install -y git curl wget zlib-devel xz-devel bzip2-devel libcurl-devel && \
12+
wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo && \
13+
yum install -y devtoolset-2-gcc devtoolset-2-binutils devtoolset-2-gcc-c++ lzma-devel glibc-static && \
14+
source scl_source enable devtoolset-2 && \
15+
echo "source scl_source enable devtoolset-2" >> ~/.bashrc && \
16+
echo "source scl_source enable devtoolset-2" >> ~/.bash_profile && \
17+
wget --quiet https://ftp.gnu.org/gnu/m4/m4-1.4.18.tar.gz && \
18+
tar xzf m4-1.4.18.tar.gz && cd m4* && ./configure && make && make install && cd .. && \
19+
rm -rf m4* && \
20+
wget --quiet http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz && \
21+
tar xzf autoconf-2.69.tar.gz && \
22+
cd autoconf* && ./configure && make && make install && cd .. && rm -rf autoconf* && \
23+
git clone --depth 1 https://github.com/ebiggers/libdeflate.git && \
24+
cd libdeflate && make -j 2 CFLAGS='-fPIC -O3' libdeflate.a && \
25+
cp libdeflate.a /usr/local/lib && cp libdeflate.h /usr/local/include && \
26+
cd .. && rm -rf libdeflate && \
27+
wget --quiet http://http.debian.net/debian/pool/main/b/bzip2/bzip2_1.0.6.orig.tar.bz2 && \
28+
tar xjvf bzip2_1.0.6.orig.tar.bz2 && \
29+
cd bzip2-1.0.6 && \
30+
make -j2 install && \
31+
cd ../ && \
32+
rm -rf bzip2-* && \
33+
wget --quiet https://www.zlib.net/zlib-1.2.11.tar.gz && \
34+
tar xzf zlib-1.2.11.tar.gz && \
35+
cd zlib-1.2.11 && \
36+
./configure && \
37+
make -j4 install && \
38+
cd .. && \
39+
rm -rf zlib* && \
40+
wget --quiet https://tukaani.org/xz/xz-5.2.4.tar.bz2 && \
41+
tar xjf xz-5.2.4.tar.bz2 && \
42+
cd xz-5.2.4 && \
43+
./configure && \
44+
make -j4 install && \
45+
cd .. && \
46+
rm -r xz*
1147

48+
49+
RUN source scl_source enable devtoolset-2 && \
50+
cd / && \
51+
wget --quiet http://www.musl-libc.org/releases/musl-1.1.21.tar.gz && \
52+
tar xvf musl-1.1.21.tar.gz && \
53+
cd musl-1.1.21 && \
54+
./configure && \
55+
make -j4 install && \
56+
rm -rf musl-*
57+
58+
59+
RUN source scl_source enable devtoolset-2 && \
60+
cd / && \
61+
wget --quiet https://www.openssl.org/source/openssl-1.1.1b.tar.gz && \
62+
tar xzvf openssl-1.1.1b.tar.gz && \
63+
cd openssl-1.1.1b && \
64+
./config && \
65+
make install && cd ../ && rm -rf openssl-1.1.1b
66+
67+
68+
RUN cd / && \
69+
git clone -b devel --depth 10 git://github.com/nim-lang/nim nim && \
70+
cd nim && \
71+
chmod +x ./build_all.sh && \
72+
scl enable devtoolset-2 ./build_all.sh && \
73+
echo 'PATH=/nim/bin:$PATH' >> ~/.bashrc && \
74+
echo 'PATH=/nim/bin:$PATH' >> ~/.bash_profile && \
75+
echo 'PATH=/nim/bin:$PATH' >> /etc/environment
76+
77+
RUN source scl_source enable devtoolset-2 && \
78+
wget --quiet https://c-ares.haxx.se/download/c-ares-1.15.0.tar.gz && \
79+
tar xzf c-ares-1.15.0.tar.gz && \
80+
cd c-ares-1.15.0 && \
81+
LIBS="-lrt" LDFLAGS="-Wl,--no-as-needed -static" ./configure --enable-static && \
82+
make LDFLAGS="-Wl,--no-as-needed -all-static -lrt -lssl -lcrypto -lc" -j4 install && \
83+
cd .. && \
84+
rm -rf c-ares-1.15.0* && \
85+
wget --quiet https://curl.haxx.se/download/curl-7.64.0.tar.gz && \
86+
tar xzf curl-7.64.0.tar.gz && \
87+
cd curl-7.64.0 && \
88+
LIBS="-ldl -lpthread -lrt -lssl -lcrypto -lcares -ldl -lc" LDFLAGS="-Wl,--no-as-needed -static" PKG_CONFIG="pkg-config --static" ./configure --disable-shared --enable-static --disable-ldap --with-ssl=/usr/local/ --disable-sspi --without-librtmp --disable-ftp --disable-file --disable-dict --disable-telnet --disable-tftp --disable-rtsp --disable-pop3 --disable-imap --disable-smtp --disable-gopher --disable-smb --without-libidn --enable-ares && \
89+
make curl_LDFLAGS=-all-static LDFLAGS="-Wl,--no-as-needed -all-static -lrt -lssl -lcrypto -lcares -ldl -lc" -j4 install && \
90+
cd ../ && \
91+
rm -rf curl-7.64.0*
92+
93+
94+
RUN source scl_source enable devtoolset-2 && \
95+
git clone https://github.com/samtools/htslib && \
96+
cd htslib && git checkout 1.9 && autoheader && autoconf && \
97+
./configure --enable-s3 --enable-libcurl --with-libdeflate && \
98+
make LDFLAGS="-Wl,--no-as-needed -lrt -lssl -lcrypto -ldl -lcares -lc" -j4 CFLAGS="-fPIC -O3 -lcrypto" install && \
99+
echo "/usr/local/lib" >> /etc/ld.so.conf && \
100+
ldconfig && \
101+
cd ../ && rm -rf htslib
102+
103+
104+
ENV PATH=:/root/.nimble/bin:/nim/bin/:$PATH:/opt/rh/devtoolset-2/root/usr/bin/
105+
106+
ADD . /src/
107+
RUN cat /src/docker/docker.nim.cfg >> /nim/config/nim.cfg && \
108+
echo "source scl_source enable devtoolset-2" >> /etc/environment && \
109+
source ~/.bashrc && cd /src/ && nimble install -y && \
110+
nimble install -y docopt && \
111+
nimble install -y c2nim@#3ec45c24585ebaed && \
112+
nim c -o:/usr/local/bin/nsb /src/docker/nsb.nim && \
113+
rm -rf /src/

Dockerfile.musl-hts-nim

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
## This dockerfile
2+
# 1. builds htslib and all dependencies currently without libcurl
3+
# 2. installs nim
4+
# 3. sets up a nim binary (nsb) that is expected to be called from an external binary (static_builder)
5+
# These facilitate building static binaries for projects using hts-nim.
6+
7+
# docker build -t brentp/musl-hts-nim:latest -f Dockerfile.musl-hts-nim .
8+
FROM alpine:3.9
9+
10+
ENV LDFLAGS=-static PKG_CONFIG='pkg-config --static'
11+
ENV curl_LDFLAGS=-all-static
12+
13+
RUN apk add curl musl build-base git autoconf zlib-dev bzip2-dev xz-dev curl-dev
14+
15+
RUN cd / && \
16+
git clone -b devel --depth 10 git://github.com/nim-lang/nim nim && \
17+
cd nim && sh ./build_all.sh
18+
19+
RUN git clone --depth 1 https://github.com/ebiggers/libdeflate.git && \
20+
cd libdeflate && make -j 2 CFLAGS='-fPIC -O3' libdeflate.a && \
21+
cp libdeflate.a /usr/local/lib && cp libdeflate.h /usr/include && \
22+
cd .. && rm -rf libdeflate && \
23+
git clone https://github.com/samtools/htslib && \
24+
cd htslib && git checkout 1.9 && autoheader && autoconf && \
25+
./configure --enable-plugins --disable-libcurl --with-libdeflate && \
26+
make -j4 install && \
27+
cd ../ && rm -rf htslib
28+
29+
ADD . /src/
30+
31+
ENV PATH=/root/.nimble/bin:/nim/bin/:$PATH
32+
33+
RUN cat /src/docker/docker.nim.cfg >> /nim/config/nim.cfg && \
34+
cd /src/ && nimble install -y && \
35+
nimble install -y docopt && \
36+
nimble install -y c2nim@#3ec45c24585ebaed && \
37+
nim c -o:/usr/local/bin/nsb /src/docker/nsb.nim && \
38+
rm -rf /src/

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,40 @@ In all cases, it's recommended to use nim version 0.18.0 or more recent.
157157

158158
Then, from this repo you can run `nimble test` and `nimble install` and then you can save the above snippets into `some.nim`
159159
and run them with `nim c -d:release -r some.nim`. This will run them and save an executable named `some`.
160+
161+
## Static Builds
162+
163+
`hts-nim` is meant to simplify and speed development and distribution. To that end, there is some machinery to help create
164+
truly static binaries for linux from nim-projects and for simple nim scripts. This means that there is no dependency on libhts.so. These builds only require docker and [this static binary XXX TODO](https://github.com/brentp/slivar/releases).
165+
166+
For a single file application that does not have a nimble file we can specify the dependencies using `--deps`:
167+
168+
```
169+
hts_nim_static_builder -s vcf_cleaner.nim --deps "hts@>=0.2.7" --deps "binaryheap"
170+
```
171+
172+
This will create a static binary at `./vcf_cleaner`.
173+
174+
175+
176+
Projects with `.nimble` files can use that directly to indicate dependencies.
177+
For example, to build [slivar](https://github.com/brentp/slivar), we can do:
178+
179+
```
180+
hts_nim_static_builder -s ../slivar/src/slivar.nim -n ../slivar/slivar.nimble
181+
```
182+
183+
After this finishes, a static `slivar` binary will appear in the current working directory.
184+
185+
We can verify that it is static using:
186+
187+
```
188+
$ file ./slivar
189+
./slivar: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, BuildID[sha1]=c2b5b52cb7be7f81bf90355a4e44a08a08df91d8, not stripped
190+
```
191+
192+
The [docker image](https://hub.docker.com/r/brentp/musl-hts-nim) is based on alpine linux and uses musl to create truly static binaries.
193+
At this time, libcurl is not supported so only binaries built using this method will only be able to access local files (no http/https/s3/gcs).
194+
195+
The docker images does use [libdeflate](https://github.com/ebiggers/libdeflate) by default. That provides,
196+
for example, a 20% speed improvement when used to build [mosdepth](https://github.com/brentp/mosdepth).

docker/docker.nim.cfg

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# in htslib: ./configure --with-libdeflate --disable-libcurl
2+
# nim c -a -d:static -d:release mosdepth.nim
3+
@if nsb_static:
4+
passC:"-static"
5+
passl:"-static"
6+
7+
passl:"/usr/lib/libm.a"
8+
passl:"/usr/local/lib/libhts.a"
9+
#passl:"/usr/local/lib/libnghttp2.a"
10+
passl:"/usr/local/lib/libdeflate.a"
11+
passl:"/usr/lib/liblzma.a"
12+
passl:"/lib/libz.a"
13+
passl:"/usr/lib/libbz2.a"
14+
passl:"/usr/lib/libpthread.a"
15+
passl:"/usr/lib/libdl.a"
16+
passl:"/usr/lib/libcurl.a"
17+
passl:"/usr/lib/libc.a"
18+
passl:"/usr/lib/libssl.a"
19+
passl:"/usr/lib/libcrypto.a"
20+
passl:"/usr/lib/libssh2.a"
21+
passl:"/usr/lib/librt.a"
22+
dynlibOverride:"hts"
23+
@end

docker/hts_nim_static_builder.nim

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# nim static builder
2+
# to be run outside the docker container
3+
import docopt
4+
import os
5+
import osproc
6+
import strformat
7+
import strutils
8+
9+
let doc = """
10+
static-builder
11+
12+
Usage: static-builder [options --deps <string>...] [--] [<nim_compiler_args>...]
13+
14+
`nim_compiler_args` are passed on directly to the nim compiler, e.g. --excessiveStackTrace:on
15+
16+
Options:
17+
18+
-n --nimble-file <string> optional path to nimble file must be in the same or parent directory of the nim source file.
19+
-s --nim-src-file <string> required path to nim file to be compiled to binary
20+
-x --debug debug build. default is to build in release mode.
21+
-d --deps <string>... any number of dependencies, e.g. --deps "hts@>=0.2.7" --deps "lapper"
22+
23+
"""
24+
25+
let args = docopt(doc)
26+
27+
echo $args
28+
29+
if $args["--nim-src-file"] == "nil":
30+
echo doc
31+
echo "source file required"
32+
quit 1
33+
34+
var source = expandFilename($args["--nim-src-file"])
35+
if not existsFile(source):
36+
quit "couldn't find source file"
37+
38+
var dir: string
39+
var nimblePath: string
40+
41+
if $args["--nimble-file"] != "nil":
42+
nimblePath = expandFileName($args["--nimble-file"])
43+
var (d, _, _) = splitFile(nimblePath)
44+
dir = d
45+
else:
46+
var (d, name, _) = splitFile(source)
47+
dir = d
48+
49+
# file gets build and sent to /load so it appears in the users pwd
50+
var cmd = &"""docker run -v {dir}:{dir} -v {getCurrentDir()}:/load/ brentp/musl-hts-nim:latest /usr/local/bin/nsb """
51+
if $args["--nimble-file"] != "nil":
52+
cmd &= &"""-n {nimblePath}"""
53+
54+
cmd &= &""" -s {source}"""
55+
56+
for d in @(args["--deps"]):
57+
cmd &= &""" --deps "{d}" """
58+
59+
var added_dash = false
60+
if len(@(args["<nim_compiler_args>"])) > 0:
61+
cmd &= &""" -- {join(@(args["<nim_compiler_args>"]), " ")}"""
62+
added_dash = true
63+
64+
if not args["--debug"]:
65+
if not added_dash:
66+
cmd &= " -- "
67+
cmd &= " -d:release"
68+
69+
echo cmd
70+
71+
if execCmd(cmd) != 0:
72+
quit "failed"

docker/nsb.nim

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# nim static builder
2+
# to be run inside the docker container
3+
import docopt
4+
import os
5+
import osproc
6+
import strformat
7+
import strutils
8+
9+
let doc = """
10+
nim-static-builder
11+
12+
Usage: nsb [options --deps <string>...] [--] [<nim_compiler_args>...]
13+
14+
Options:
15+
16+
-n --nimble-file <string> optional path to nimble file
17+
-s --nim-src-file <string> required path to nim file to be compiled to binary
18+
-d --deps <string>... any number of dependencies, e.g. --deps "hts@>=0.2.7" --deps "lapper"
19+
20+
"""
21+
22+
echo commandLineParams()
23+
24+
let args = docopt(doc)
25+
26+
echo $args
27+
28+
if $args["--nim-src-file"] == "nil":
29+
echo doc
30+
echo "source file required"
31+
quit 1
32+
33+
var source = expandFilename($args["--nim-src-file"])
34+
if not existsFile(source):
35+
quit "couldn't find source file"
36+
37+
var path = getEnv("PATH")
38+
path &= ":/nim/bin"
39+
putEnv("PATH", path)
40+
41+
for d in @(args["--deps"]):
42+
if 0 != execCmd(&"""nimble install -y "{d}" """):
43+
quit "failed on nimble install of " & d
44+
45+
if $args["--nimble-file"] != "nil":
46+
var (dir, _, _) = splitFile(expandFileName($args["--nimble-file"]))
47+
dir.setCurrentDir
48+
49+
if execCmd(&"""sh -c "export PATH={path}; /nim/bin/nimble install -d -y " """) != 0:
50+
quit "coudn't run nimble install"
51+
52+
53+
var (dir, name, _) = splitFile(source)
54+
dir.setCurrentDir
55+
56+
removeFile("xx_exe_out")
57+
var cmd = &"""/nim/bin/nim c -d:nsb_static {join(@(args["<nim_compiler_args>"]), " ")} -o:xx_exe_out {name}"""
58+
if execCmd(&"""sh -c "{cmd}" """) != 0:
59+
quit "error compiling code"
60+
61+
copyFileWithPermissions("xx_exe_out", &"/load/{name}")
62+
removeFile("xx_exe_out")
63+
64+
echo &"wrote executable: {name}"

src/hts/bam.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ./private/hts_concat
22
include "./bam/enums"
3+
import strformat
34
import strutils
45
include "./bam/flag"
56
include "./bam/cigar"
@@ -323,7 +324,7 @@ proc load_index*(b: Bam, path: string) =
323324
else:
324325
b.idx = sam_index_load2(b.hts, b.path, path.cstring)
325326
if b.idx == nil:
326-
raise newException(IoError, "[bam] load_index error opening index %s for bam %s. %s" % [path, $b.path, $strerror(errno)])
327+
raise newException(IoError, &"[bam] load_index error opening index {path} for bam {b.path}. {strerror(errno)}")
327328

328329
proc hts_set_opt*(fp: ptr htsFile; opt: FormatOption): cint {.varargs, cdecl,
329330
importc: "hts_set_opt", dynlib: libname.}

0 commit comments

Comments
 (0)