From d531d1bfddfe0a89a340428356d7ae1bcc0d34f7 Mon Sep 17 00:00:00 2001 From: Frederik Zorn Date: Sun, 12 Nov 2023 15:16:56 +0100 Subject: [PATCH] refactor(structure)!: new image name * anki-sync-server now named anki-sync-server not oci-anki-sync * multi-arch builds * convert docker-style to cargo-style * cross-compile on linux/amd64 * install gcc cross-compiler + rustup target for target arch * set gcc linker for specific arch * build glibc applications BREAKING CHANGE: new container name Signed-off-by: Frederik Zorn --- .github/dependabot.yml | 12 -- .github/workflows/container-build.yml | 115 +++++++++++------- ...te-anki-version.yml => update-version.yml} | 24 ++-- .reuse/dep5 | 6 +- README.md | 48 +++++--- SETUP.md | 17 +-- .../artifacthub-repo.yml | 2 +- anki-sync-server/containerfile | 87 +++++++++++++ .../imgs/ah-logo.png | Bin values.yml => anki-sync-server/values.yml | 2 +- containerfile | 52 -------- tools/convert-arch-cargo.sh | 32 +++++ 12 files changed, 248 insertions(+), 149 deletions(-) rename .github/workflows/{update-anki-version.yml => update-version.yml} (66%) rename artifacthub-repo.yml => anki-sync-server/artifacthub-repo.yml (90%) create mode 100644 anki-sync-server/containerfile rename imgs/anki/logo.png => anki-sync-server/imgs/ah-logo.png (100%) rename values.yml => anki-sync-server/values.yml (89%) delete mode 100644 containerfile create mode 100644 tools/convert-arch-cargo.sh diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b767cda..5371df4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,15 +20,3 @@ updates: # prefix all commit messages with "chore(deps): " commit-message: prefix: "chore(deps): " - - # Create a group of dependencies to be updated together in one pull request - groups: - # Update all dependencies from redhat-actions together - redhat-actions: - patterns: - - "redhat-actions/*" - - # Update all non-specified dependencies together - all: - patterns: - - "*" diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml index 680f197..bde716d 100644 --- a/.github/workflows/container-build.yml +++ b/.github/workflows/container-build.yml @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -name: 'Build and push anki-sync container' +name: 'Build and push image' # run workflow on every branch, only upload on main (allow manual trigger) on: [push, pull_request, workflow_dispatch] @@ -27,10 +27,17 @@ jobs: uses: mikefarah/yq@v4 - name: Setup oras + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' uses: oras-project/setup-oras@v1 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GitHub Container Registry - uses: redhat-actions/podman-login@v1 + uses: docker/login-action@v3 if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' with: registry: ghcr.io @@ -38,10 +45,9 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Login to DockerHub - uses: redhat-actions/podman-login@v1 + uses: docker/login-action@v3 if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' with: - registry: docker.io username: federdaemn password: ${{ secrets.DOCKER_TOKEN }} @@ -49,7 +55,7 @@ jobs: id: set-op run: | # extract version from ./values.yml - echo "anki-version=$(yq '.version.anki-version' ./values.yml)" \ + echo "software-version=$(yq '.version.software-version' ./anki-sync-server/values.yml)" \ >> $GITHUB_OUTPUT # get current date+time @@ -58,59 +64,76 @@ jobs: # get current date+time docker tag compatible echo "time-docker=$(date --utc +'%Y-%m-%dt%H-%M-%Sz')" >> $GITHUB_OUTPUT - - name: Build image - id: build_image - uses: redhat-actions/buildah-build@v2 + - name: Build and push image in production + uses: docker/build-push-action@v5 + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' with: + # directly push image + push: true + + # use local repository + context: . + # which containerfile to build from - containerfiles: | - ./containerfile + file: | + ./anki-sync-server/containerfile + + # build args to pass to the build + build-args: | + software_version=${{ steps.set-op.outputs.software-version }} + + # build for many platforms at the same time + platforms: | + linux/amd64 + linux/arm64 # set labels to append to the image labels: | - # opencontainer labels specified - # https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys - org.opencontainers.image.title=oci-anki-sync - org.opencontainers.image.description=Anki sync server (anki-sync-server) in a container - org.opencontainers.image.authors=Frederik Zorn org.opencontainers.image.created=${{ steps.set-op.outputs.time }} - org.opencontainers.image.version=${{ steps.set-op.outputs.anki-version }} - org.opencontainers.image.licenses=AGPL-3.0-or-later - org.opencontainers.image.url=https://github.com/federdaemn/oci-anki-sync - org.opencontainers.image.source=https://github.com/federdaemn/oci-anki-sync - - # artfifacthub labels specified - # https://artifacthub.io/docs/topics/repositories/container-images/#image-metadata - io.artifacthub.package.readme-url=https://raw.githubusercontent.com/federdaemn/oci-anki-sync/main/README.md - io.artifacthub.package.maintainers=[{"name":"federdaemn","email":"federdaemn@mail.de"}] - io.artifacthub.package.keywords=anki,sync,server,anki-sync-server,anki-sync - io.artifacthub.package.logo-url=https://raw.githubusercontent.com/federdaemn/oci-anki-sync/main/imgs/anki/logo.png - io.artifacthub.package.alternative-locations=docker.io/federdaemn/oci-anki-sync - - # build args to pass to the build - build-args: | - anki_version=${{ steps.set-op.outputs.anki-version }} + org.opencontainers.image.version=${{ steps.set-op.outputs.software-version }} # tags to build the image with tags: | - ghcr.io/federdaemn/oci-anki-sync:latest - ghcr.io/federdaemn/oci-anki-sync:${{ steps.set-op.outputs.anki-version }} - ghcr.io/federdaemn/oci-anki-sync:${{ steps.set-op.outputs.time-docker }} - docker.io/federdaemn/oci-anki-sync:latest - docker.io/federdaemn/oci-anki-sync:${{ steps.set-op.outputs.anki-version }} - docker.io/federdaemn/oci-anki-sync:${{ steps.set-op.outputs.time-docker }} - - - name: Push image - uses: redhat-actions/push-to-registry@v2 - if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' - with: - tags: ${{ steps.build_image.outputs.tags }} + ghcr.io/federdaemn/anki-sync-server:latest + ghcr.io/federdaemn/anki-sync-server:${{ steps.set-op.outputs.software-version }} + ghcr.io/federdaemn/anki-sync-server:${{ steps.set-op.outputs.time-docker }} + docker.io/federdaemn/anki-sync-server:latest + docker.io/federdaemn/anki-sync-server:${{ steps.set-op.outputs.software-version }} + docker.io/federdaemn/anki-sync-server:${{ steps.set-op.outputs.time-docker }} - # use oras to push artifacthub metadata to ghcr.io + - name: Build image in testing only + uses: docker/build-push-action@v5 + if: github.event_name == 'pull_request' + with: + # do not push image + push: false + + # use local repository + context: . + + # which containerfile to build from + file: | + ./anki-sync-server/containerfile + + # build args to pass to the build + build-args: | + software_version=${{ steps.set-op.outputs.software-version }} + + # build for many platforms at the same time + platforms: | + linux/amd64 + linux/arm64 + + # use oras to push artifacthub metadata to registries # source: https://artifacthub.io/docs/topics/repositories/container-images/#repository-metadata - name: Push metadata + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' run: | oras push \ - ghcr.io/federdaemn/oci-anki-sync:artifacthub.io \ + ghcr.io/federdaemn/anki-sync-server:artifacthub.io \ + --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \ + anki-sync-server/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml + oras push \ + docker.io/federdaemn/anki-sync-server:artifacthub.io \ --config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \ - artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml + anki-sync-server/artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml diff --git a/.github/workflows/update-anki-version.yml b/.github/workflows/update-version.yml similarity index 66% rename from .github/workflows/update-anki-version.yml rename to .github/workflows/update-version.yml index f884f80..a2086af 100644 --- a/.github/workflows/update-anki-version.yml +++ b/.github/workflows/update-version.yml @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: Apache-2.0 -name: 'Update anki version in values.yml' +name: 'Update anki-sync-server version in values.yml (GH API)' # run workflow every monday at 00:00 UTC on: @@ -12,7 +12,7 @@ on: jobs: update-version: - name: Update anki version in values.yml + name: Update anki-sync-server version in values.yml # use ubuntu as runner runs-on: ubuntu-latest @@ -34,10 +34,10 @@ jobs: - name: Setup yq uses: mikefarah/yq@v4 - - name: Get current anki version - id: anki-version + - name: Get current anki-sync-server version + id: set-op run: | - echo "anki-version=$(yq '.version.anki-version' ./values.yml)" \ + echo "software-version=$(yq '.version.software-version' ./anki-sync-server/values.yml)" \ >> $GITHUB_OUTPUT - name: Fetch GH API and get tag_name from latest release @@ -53,18 +53,18 @@ jobs: # set output for later use echo "name=$tag_name" >> $GITHUB_OUTPUT - - name: Set new anki version in values.yml - if: ${{ steps.anki-version.outputs.anki-version != steps.get-release.outputs.name }} + - name: Set new anki-sync-server version in values.yml + if: ${{ steps.set-op.outputs.software-version != steps.get-release.outputs.name }} run: | - yq -i '.version.anki-version = "${{ steps.get-release.outputs.name }}"' \ - ./values.yml + yq -i '.version.software-version = "${{ steps.get-release.outputs.name }}"' \ + ./anki-sync-server/values.yml - name: Set up git email/name a. commit changes - if: ${{ steps.anki-version.outputs.anki-version != steps.get-release.outputs.name }} + if: ${{ steps.set-op.outputs.software-version != steps.get-release.outputs.name }} run: | git config user.name federdaemn-bot git config user.email federdaemn@mail.de - git add ./values.yml - git commit -m "chore(anki): bump anki version to ${{ steps.get-release.outputs.name }}" \ + git add ./anki-sync-server/values.yml + git commit -m "chore(anki-sync-server): bump version to ${{ steps.get-release.outputs.name }}" \ -m "Signed-off-by: github-actions in the name of Frederik Zorn " git push origin main diff --git a/.reuse/dep5 b/.reuse/dep5 index 6bdd5d5..6acf958 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -1,7 +1,7 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: oci-anki-sync +Upstream-Name: docker-anki-sync-server Upstream-Contact: Frederik Zorn -Source: https://github.com/federdaemn/oci-anki-sync +Source: https://github.com/federdaemn/docker-anki-sync-server # Sample paragraph, commented out: # @@ -13,6 +13,6 @@ Files: .vscode/* Copyright: 2023 Frederik Zorn License: Apache-2.0 -Files: imgs/anki/logo.png +Files: anki-sync-server/imgs/ah-logo.png Copyright: Alex Fraser https://apps.ankiweb.net/ License: LicenseRef-anki-logo diff --git a/README.md b/README.md index 9c142af..54600d8 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,30 @@ SPDX-FileCopyrightText: 2023 Frederik Zorn SPDX-License-Identifier: Apache-2.0 --> -# oci-anki-sync (anki-sync-server docker container) repository +# docker anki-sync-server repository -This contains a Dockerfile and Ci scripts to build a container image for +This contains a dockerfile and CI/CD scripts to build a container image for [Anki Sync Server](https://apps.ankiweb.net/) using a scratch image as base image. +## Warning + +This image is not official. Use at your own risk. + +## Images + +## Host Architecture + +This image is cross-compiled and thus available for + +* linux/amd64 +* linux/arm64 + +I sadly can not build for linux/arm/v7 +(because of [build errors](https://github.com/rust-lang/cargo/issues/9545#issue-911773248)) +and for linux/riscv64 (because of missing +[rust](https://hub.docker.com/_/rust/tags) container support). + ## Updates and Versions * The container is auto-updated every Monday whenever a new version of anki is @@ -26,24 +44,26 @@ can forget this project exists :). ### Git This project is hosted on [GitHub](https://github.com) at the -[federdaemn/oci-anki-sync](https://github.com/federdaemn/oci-anki-sync) project. +[federdaemn/docker-anki-sync-server](https://github.com/federdaemn/docker-anki-sync-server) +project. ### Container Registry -The container images are hosted on -[GitHub Container Registry](https://github.com/federdaemn/oci-anki-sync/pkgs/container/oci-anki-sync) +You can find this image on +[GitHub Container Registry](https://github.com/federdaemn/docker-anki-sync-server/pkgs/container/anki-sync-server) (recommended) and on -[Docker Hub](https://hub.docker.com/r/federdaemn/oci-anki-sync) for redundancy. -Additionally, the container images are also listed on -[artifacthub.io](https://artifacthub.io) with the package name -[`oci-anki-sync`](https://artifacthub.io/packages/container/oci-anki-sync/oci-anki-sync). +[Docker Hub](https://hub.docker.com/r/federdaemn/anki-sync-server) for +redundancy. The Artifact Hub name is +[`anki-sync-server`](https://artifacthub.io/packages/container/anki-sync-server/anki-sync-server). -There are three tags available on both registries: +There are four tags available: * `latest`: Always the latest version of anki. * ``: The version specified of anki. -* ``: The exact time(+date) when the container was built +* ``: The exact time (+ date) when the container was built (using (`date` syntax): %Y-%m-%dt%H-%M-%Sz). +* `artifacthub.io`: You may ignore this. This is just for Artifact Hub Verified + Publisher. ## Setup @@ -51,9 +71,9 @@ There are three tags available on both registries: ## Contributing -* Contributions for newer versions or files are gracefully accepted but the - scope is to only generate an auto-updating docker/oci container. -* Please try to wrap lines at 80 characters. +* Contributions for newer versions or files are gracefully accepted. Even things + like small speed improvements are helpfull. +* Please try to wrap all lines at 80 characters. ## License diff --git a/SETUP.md b/SETUP.md index 0210c11..654d067 100644 --- a/SETUP.md +++ b/SETUP.md @@ -13,29 +13,30 @@ version: "3.7" services: - oci-anki-sync: - # https://github.com/federdaemn/oci-anki-sync/blob/main/SETUP.md - image: ghcr.io/federdaemn/oci-anki-sync:2.1.66 - container_name: oci-anki-sync + anki-sync-server: + # https://github.com/federdaemn/anki-sync-server/blob/main/SETUP.md + image: ghcr.io/federdaemn/anki-sync-server:2.1.66 + container_name: anki-sync-server restart: unless-stopped # these are sample passwords, please change them environment: - SYNC_USER1=panda:rsfPz4NXELBxmJ - SYNC_USER2=penguin:2Qtf5nnsDpsQ3b volumes: - - oci-anki-sync:/config + - anki-sync-server:/config ports: - 22701:22701 volumes: - oci-anki-sync: + anki-sync-server: ``` * the important parts are * set SYNC_USERX to your desired username and password - * mount volume oci-anki-sync to /config + * mount volume anki-sync-server to /config * open port 22701 **for more configuration options see ** -If you know how to configure another reverse-proxy please open an issue/pull request +If you know how to configure another reverse-proxy please open an issue/pull +request. diff --git a/artifacthub-repo.yml b/anki-sync-server/artifacthub-repo.yml similarity index 90% rename from artifacthub-repo.yml rename to anki-sync-server/artifacthub-repo.yml index c685939..c516e03 100644 --- a/artifacthub-repo.yml +++ b/anki-sync-server/artifacthub-repo.yml @@ -9,7 +9,7 @@ # # optional, enables verified publisher -repositoryID: 1c0df0d3-21af-4cb9-aaae-663cdaf93807 +repositoryID: 0e6be73b-431a-4bd8-9220-26400991eee6 # optional, used to claim repository ownership owners: diff --git a/anki-sync-server/containerfile b/anki-sync-server/containerfile new file mode 100644 index 0000000..368ffce --- /dev/null +++ b/anki-sync-server/containerfile @@ -0,0 +1,87 @@ +# SPDX-FileCopyrightText: 2023 Frederik Zorn +# +# SPDX-License-Identifier: Apache-2.0 + +# use the official Rust image with debian base as builder +FROM --platform=$BUILDPLATFORM docker.io/library/rust:latest as builder + +# set anki version +ARG software_version + +# make multi-platform args available to process +ARG TARGETARCH +ARG TARGETVARIANT + +# see script +COPY ../tools/convert-arch-cargo.sh . +RUN sh ./convert-arch-cargo.sh + +# upgrade system and install dependencies: +# protocol buffer compiler + gcc/g++ compiler for specific arch +RUN \ + apt-get update && \ + apt-get upgrade -y && \ + apt-get install protobuf-compiler $(cat /.c-compiler.txt) -y + +# git clone source repository of anki (only last commit, init submodules) +RUN \ + git clone --recurse-submodules --depth 1 --branch $software_version \ + https://github.com/ankitects/anki.git \ + /build + +# set build directory (like cd) +WORKDIR /build + +# build anki-sync-server +# install needed rustup targets +# set PROTOC to use the installed protobuf compiler (error if not set) +# set linker for specific arch (https://github.com/rust-lang/rust/issues/28924#issuecomment-442260036) + +# set RUSTFLAGS (--target needed) to build static binary +# source: https://msfjarvis.dev/posts/building-static-rust-binaries-for-linux/ + +# build with release optimizations +# copy binary to standard dir (use cargo build --out when stable) +RUN \ + rustup target add $(cat /.cargo-platform.txt) && \ + PROTOC=/usr/bin/protoc \ + $(cat /.cargo-linker.txt) RUSTFLAGS='-C target-feature=+crt-static' \ + cargo build --release --target $(cat /.cargo-platform.txt) \ + --package anki-sync-server && \ + mkdir -p /output && \ + cp /build/target/$(cat /.cargo-platform.txt)/release/anki-sync-server /output/anki-sync-server + +# copy binary container with nothing other to reduce container size +FROM scratch + +# copy binary from builder +COPY --from=builder /output/anki-sync-server /app/anki-sync-server + +ENV \ +# stores data in /config (VOLUME for persistence) + SYNC_BASE="/config" \ +# set default port + SYNC_PORT="27701" + +# switch user for better security +USER anki-sync-server + +# don't forget to set at least SYNC_USER1 +CMD [ "/app/anki-sync-server" ] + +# set labels required for artifacthub.io +LABEL \ +# opencontainer labels specified +# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys + "org.opencontainers.image.title"="anki-sync-server" \ + "org.opencontainers.image.description"="Anki Sync Server (anki-sync-server) docker/oci container" \ + "org.opencontainers.image.authors"="Frederik Zorn " \ + "org.opencontainers.image.source"="https://github.com/federdaemn/docker-anki-sync-server" \ + "org.opencontainers.image.licenses"="AGPL-3.0-or-later" \ +# artfifacthub labels specified +# https://artifacthub.io/docs/topics/repositories/container-images/#image-metadata + "io.artifacthub.package.readme-url"="https://raw.githubusercontent.com/federdaemn/docker-anki-sync-server/main/README.md" \ + "io.artifacthub.package.maintainers"="[{"name":"federdaemn","email":"federdaemn@mail.de"}]" \ + "io.artifacthub.package.logo-url"="https://raw.githubusercontent.com/federdaemn/docker-anki-sync-server/main/anki-sync-server/imgs/ah-logo.png" \ + "io.artifacthub.package.alternative-locations"="docker.io/federdaemn/anki-sync-server" \ + "io.artifacthub.package.keywords"="anki,sync,server,anki-sync-server,anki-sync" diff --git a/imgs/anki/logo.png b/anki-sync-server/imgs/ah-logo.png similarity index 100% rename from imgs/anki/logo.png rename to anki-sync-server/imgs/ah-logo.png diff --git a/values.yml b/anki-sync-server/values.yml similarity index 89% rename from values.yml rename to anki-sync-server/values.yml index 3bb79ac..3bbfbf5 100644 --- a/values.yml +++ b/anki-sync-server/values.yml @@ -7,4 +7,4 @@ version: # anki version to compile and to use as tag - anki-version: 2.1.66 + software-version: 2.1.66 diff --git a/containerfile b/containerfile deleted file mode 100644 index d93abc3..0000000 --- a/containerfile +++ /dev/null @@ -1,52 +0,0 @@ -# SPDX-FileCopyrightText: 2023 Frederik Zorn -# -# SPDX-License-Identifier: Apache-2.0 - -# use the official Rust image with debian base as builder -FROM docker.io/library/rust:latest as builder - -# set anki version -ARG anki_version - -# upgrade system a. install dependency -RUN \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get install protobuf-compiler -y - -# git clone source repository of anki (only last commit, init submodules) -RUN \ - git clone https://github.com/ankitects/anki.git \ - --branch $anki_version --depth 1 --recurse-submodules \ - /build - -# Set build directory (like cd) -WORKDIR /build - -# build anki-sync-server -# set PROTOC to use the installed protobuf compiler (error if not set) - -# set RUSTFLAGS (--target needed) to build static binary -# source: https://msfjarvis.dev/posts/building-static-rust-binaries-for-linux/ - -# build with release optimizations -RUN \ - PROTOC=/usr/bin/protoc RUSTFLAGS='-C target-feature=+crt-static' \ - cargo build --release --target x86_64-unknown-linux-gnu \ - --package anki-sync-server - -# copy binary container with nothing other to reduce container size -FROM scratch - -# copy binary from builder -COPY --from=builder /build/target/x86_64-unknown-linux-gnu/release/anki-sync-server /app/anki-sync-server - -ENV \ -# stores data in /config (VOLUME for persistence) - SYNC_BASE="/config" \ - -# set default port - SYNC_PORT="27701" - -# don't forget to set at least SYNC_USER1 -CMD [ "/app/anki-sync-server" ] diff --git a/tools/convert-arch-cargo.sh b/tools/convert-arch-cargo.sh new file mode 100644 index 0000000..021d70b --- /dev/null +++ b/tools/convert-arch-cargo.sh @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2023 Frederik Zorn +# +# SPDX-License-Identifier: Apache-2.0 + +# convert docker-style target architecture (e.g. linux/arm/v7) to +# cargo-style target architecture (e.g. armv7-unknown-linux-gnueabihf) + +# source: https://blog.container-solutions.com/building-multiplatform-container-images + +#!/usr/bin/env sh + +# merge both variables with a / as seperator if the latter exists +if [ -n "$TARGETVARIANT" ]; then + TARGET="$TARGETARCH/$TARGETVARIANT" + else + TARGET="$TARGETARCH" +fi + +# write cargo compatible archs to /.cargo-platform.txt +case $TARGET in + + "amd64") + echo "x86_64-unknown-linux-gnu" > /.cargo-platform.txt + echo "" > /.cargo-linker.txt + echo "gcc g++" > /.c-compiler.txt + ;; + "arm64") + echo "aarch64-unknown-linux-gnu" > /.cargo-platform.txt + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" > /.cargo-linker.txt + echo "gcc-aarch64-linux-gnu g++-aarch64-linux-gnu" > /.c-compiler.txt + ;; +esac