diff --git a/.gitignore b/.gitignore index d465105..c7b6a11 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ target/ .bloop .metals +metals.sbt project/project + +archive/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1564534 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,54 @@ +FROM debian:11-slim as build-env + +# We don't use https://github.com/sbt/docker-sbt so that we can chose specific base image +# https://hub.docker.com/_/eclipse-temurin +ENV JAVA_HOME=/opt/java/openjdk +COPY --from=eclipse-temurin:17 $JAVA_HOME $JAVA_HOME +ENV PATH="${JAVA_HOME}/bin:${PATH}" + +WORKDIR /build + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl ca-certificates unzip git \ + clang llvm \ + # for building s2n + http4s native + libssl-dev cmake build-essential + +# Install sbt +ARG SBT_VERSION=1.9.6 +RUN \ + curl -sL "https://github.com/sbt/sbt/releases/download/v${SBT_VERSION}/sbt-${SBT_VERSION}.zip" > /tmp/sbt.zip && \ + unzip /tmp/sbt.zip -d /opt +ENV PATH="/opt/sbt/bin:${PATH}" + +# Install s2n. +# We don't use linuxbrew because +# 1. linuxbrew doesn't support ARM64 yet https://docs.brew.sh/Homebrew-on-Linux#arm-unsupported +# 2. sbt doesn't work on qemu (so we can't use --platform) because of https://github.com/docker/for-mac/issues/6174 +# Therefore we build s2n from source https://github.com/aws/s2n-tls/blob/e4f5bf6e779c153d9063805be81c547584398f7a/docs/BUILD.md +ARG S2N_VERSION=1.3.55 +RUN git clone https://github.com/aws/s2n-tls.git --branch v$S2N_VERSION +RUN cd s2n-tls && \ + cmake . -Bbuild \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=./s2n-tls-install && \ + cmake --build build -j $(nproc) && \ + CTEST_PARALLEL_LEVEL=$(nproc) ctest --test-dir build && \ + cmake --install build + +ARG ARCHIVE +ADD $ARCHIVE /build +RUN S2N_LIBRARY_PATH=/build/s2n-tls/s2n-tls-install/lib sbt nativeLink + +FROM debian:11-slim +WORKDIR /app +COPY --from=build-env /build/target/scala-3.3.1/scala-native-ember-example-out ./app +# RUN ldd /app/app +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + libssl-dev && \ + apt-get clean && rm -rf /var/lib/apt/lists/* +EXPOSE 8080 +ENV S2N_DONT_MLOCK=1 +CMD ["./app"] diff --git a/build-image.sh b/build-image.sh new file mode 100755 index 0000000..8648555 --- /dev/null +++ b/build-image.sh @@ -0,0 +1,15 @@ +#!/bin/bash +mkdir -p archive + +untracked=$(git ls-files -o --exclude-standard) +if [[ -n "$untracked" ]]; then + # there are untracked files + rev=$(git stash create | cut -c1-6) +else + rev=$(git rev-parse HEAD | cut -c1-6) +fi +echo "Creating git archive for current changes of ${rev}" +git archive -o archive/$rev.tar $rev +find archive -type f -not -name "$rev.tar" -delete + +docker build . -t ember-app --build-arg ARCHIVE=archive/$rev.tar \ No newline at end of file diff --git a/build.sbt b/build.sbt index c636e04..657d3b3 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,6 @@ -ThisBuild / scalaVersion := "3.2.0" +import scala.collection.mutable.ListBuffer + +ThisBuild / scalaVersion := "3.3.1" ThisBuild / version := "0.1.0-SNAPSHOT" ThisBuild / organization := "io.chrisdavenport" ThisBuild / organizationName := "Christopher Davenport" @@ -18,15 +20,24 @@ libraryDependencies ++= Seq( val isLinux = Option(System.getProperty("os.name")).exists(_.toLowerCase().contains("linux")) val isMacOs = Option(System.getProperty("os.name")).exists(_.toLowerCase().contains("mac")) val isArm = Option(System.getProperty("os.arch")).exists(_.toLowerCase().contains("aarch64")) +val s2nLibPath = sys.env.get("S2N_LIBRARY_PATH") + + nativeConfig ~= { c => - if (isLinux) { // brew-installed s2n - c.withLinkingOptions(c.linkingOptions :+ "-L/home/linuxbrew/.linuxbrew/lib") - } else if (isMacOs) // brew-installed OpenSSL - if(isArm) c.withLinkingOptions(c.linkingOptions :+ "-L/opt/homebrew/opt/openssl@3/lib") - else c.withLinkingOptions(c.linkingOptions :+ "-L/usr/local/opt/openssl@3/lib") - else c + val linkOpts = ListBuffer.empty[String] + if (isLinux) // brew-installed s2n + linkOpts.append("-L/home/linuxbrew/.linuxbrew/lib") + else if (isMacOs) // brew-installed OpenSSL + if(isArm) linkOpts.append("-L/opt/homebrew/opt/openssl@3/lib") + else linkOpts.append("-L/usr/local/opt/openssl@3/lib") + s2nLibPath match { + case None => + case Some(path) => linkOpts.append(s"-L$path") + } + c.withLinkingOptions(c.linkingOptions ++ linkOpts.toSeq) } + envVars ++= { val ldLibPath = if (isLinux) diff --git a/project/plugins.sbt b/project/plugins.sbt index fcf14dc..6913a59 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1 @@ -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") \ No newline at end of file +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.16") diff --git a/src/main/scala/example/EmberExample.scala b/src/main/scala/example/EmberExample.scala index 7db31f9..8ab4d30 100644 --- a/src/main/scala/example/EmberExample.scala +++ b/src/main/scala/example/EmberExample.scala @@ -25,11 +25,14 @@ object EmberExample extends EpollApp { jsonOf } - def run(args: List[String]): IO[ExitCode] = customTLS - .flatMap(createClient) - .use{ client => - createServer(client).useForever - }.as(ExitCode.Success) + def run(args: List[String]): IO[ExitCode] = { + println("Starting Ember Example") + customTLS + .flatMap(createClient) + .use{ client => + createServer(client).useForever + }.as(ExitCode.Success) + } def createServer(client: Client[IO]): Resource[IO, Unit] =