Linux 上で Objective-C 2.0 の開発環境を整える

トレンドが Swift に移って久しく，近年は Objective-C に関心を持つ人が減っていますが，Linux 上で Objective-C の開発環境を整える方法についてまとめてみました。

Docker 上の Ubuntu 18.04 および 19.10 で動作確認しています。Dockerfile の `ENV` と `RUN` の内容を取り出せば，実機でも動くでしょう。

# Objective-C 1.0 でよいなら……

昔ながらの Objective-C 1.0 でよいなら，apt で入手できる出来合いのパッケージをインストールすれば，比較的容易に Clang 9 による Objective-C の開発環境を整えられます。

```dockerfile:Dockerfile
FROM ubuntu:19.10

RUN set -x \
  && apt update \
  && apt upgrade -y \
  && DEBIAN_FRONTEND=noninteractive apt install -y tzdata \
  && apt install -y clang-9 make gobjc-9 gnustep-devel \
  && apt autoremove -y \
  && apt clean \
  && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

# Set alias
RUN echo 'alias clang-objc="clang-9 \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -lgnustep-base -I/usr/lib/gcc/x86_64-linux-gnu/9/include"' > ~/.bashrc

CMD ["/bin/bash"]
```

本質部は

```sh
$ apt install -y clang-9 make gobjc-9 gnustep-devel
```

です。また，`~/.bashrc` に

```sh
alias clang-objc="clang-9 \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -lgnustep-base -I/usr/lib/gcc/x86_64-linux-gnu/9/include"
```

と定義してあります。これにより，この Docker コンテナ内で

```sh
$ clang-objc -o hoge hoge.m
$ ./hoge
```

などとすることで簡単に Objective-C のソース `hoge.m` をコンパイル・実行できます。

# Objective-C 2.0 のフル機能を使いたいなら……

* Automatic Reference Counting (ARC)
* Blocks
* Dot Notation
* Object Literal
* Object Subscripting
* Fast Enumeration
* Lightweight Generics
* Grand Central Dispatch (GCD)

といった Modern Objective-C の機能をフルに使いたい場合は，GNUstep の libobjc2 をセットアップせねばなりません。これは apt でインストールできないので，ソースからビルドする必要があります。これがなかなか大変です。[plaurent 氏の GitHub レポジトリ](https://github.com/plaurent/gnustep-build)が大変参考になりました。ビルド手順を Dockerfile にまとめると次のようになります。

```dockerfile:Dockerfile
FROM ubuntu:19.10

ENV CC clang-9
ENV CXX clang++-9
ENV CXXFLAGS -std=c++11
ENV RUNTIME_VERSION gnustep-2.0
ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
ENV LD /usr/bin/ld.gold
ENV LDFLAGS "-fuse-ld=/usr/bin/ld.gold -L/usr/local/lib"

WORKDIR /GNUstep-build

RUN set -x \
  && apt update \
  && apt upgrade -y \
  && apt install -y git sudo clang-9 clang++-9 build-essential wget \
    subversion cmake libffi-dev libxml2-dev \
    libgnutls28-dev libicu-dev libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev autoconf libtool \
    libjpeg-dev libtiff-dev libffi-dev libcairo-dev libx11-dev libxt-dev libxft-dev \
  && apt autoremove -y \
  && apt clean \
  && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

# Build GNUstep make
RUN set -x \
    #
    # Checkout tools-make
    && git clone https://github.com/gnustep/tools-make.git \
    #
    # Build GNUstep make
    && cd tools-make \
    && CC=${CC} ./configure \
            --with-layout=gnustep \
            --disable-importing-config-file \
            --enable-native-objc-exceptions \
            --enable-objc-arc \
            --enable-install-ld-so-conf \
            --with-library-combo=ng-gnu-gnu \
    && make -j8 \
    && sudo -E make install \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    && echo ". /usr/GNUstep/System/Library/Makefiles/GNUstep.sh" >> ~/.bashrc \
    && echo "export RUNTIME_VERSION=gnustep-2.0" >> ~/.bashrc \
    && echo 'export CXXFLAGS="-std=c++11"' >> ~/.bashrc \
    && cd .. \
#
# Build libdispatch
    #
    # Checkout swift-corelibs-libdispatch
    && git clone https://github.com/apple/swift-corelibs-libdispatch \
    && cd swift-corelibs-libdispatch \
    && git checkout swift-5.1.1-RELEASE \
    #
    # Build libdispatch
    && rm -Rf build \
    && mkdir build \
    && cd build \
    && cmake .. \
         -DCMAKE_C_COMPILER=${CC} \
         -DCMAKE_CXX_COMPILER=${CXX} \
         -DCMAKE_BUILD_TYPE=Release \
         -DUSE_GOLD_LINKER=YES \
    && make \
    && sudo -E make install \
    && sudo ldconfig \
    && cd ../.. \
#
# Build libobjc2
    #
    # Checkout libobjc2
    && git clone https://github.com/gnustep/libobjc2.git \
    && cd libobjc2 \
    && git submodule init \
    && git submodule sync \
    && git submodule update \
    #
    # Build libobjc2
    && rm -Rf build \
    && mkdir build \
    && cd build \
    && cmake .. \
         -DCMAKE_C_COMPILER=${CC} \
         -DCMAKE_CXX_COMPILER=${CXX} \
         -DCMAKE_ASM_COMPILER=${CC} \
         -DTESTS=OFF \
    && cmake --build . \
    && sudo -E make install \
    && sudo ldconfig \
    && cd ../.. \
#
# Build GNUstep make second time
    && cd tools-make \
    && CC=${CC} ./configure \
            --with-layout=gnustep \
            --disable-importing-config-file \
            --enable-native-objc-exceptions \
            --enable-objc-arc \
            --enable-install-ld-so-conf \
            --with-library-combo=ng-gnu-gnu \
    && make -j8 \
    && sudo -E make install \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    && cd .. \
#
# Build GNUstep base
    #
    # Checkout libs-base
    && git clone https://github.com/gnustep/libs-base.git \
    #
    # Build GNUstep base
    && cd libs-base \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd .. 

# For GUI programming
#
# Build GNUstep GUI Library
RUN set -x \
    && . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh \
    #
    # Checkout libs-gui
    && git clone https://github.com/gnustep/libs-gui.git \
    #
    # Build GNUstep GUI Library
    && cd libs-gui \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd .. \
#
# Build GNUstep GUI Backend
    #
    # Checkout libs-back
    && git clone https://github.com/gnustep/libs-back.git \
    #
    # Build GNUstep GUI Backend
    && cd libs-back \
    && ./configure \
    && make -j8 \
    && sudo -E make install \
    && cd ..

WORKDIR /workdir

# Clean build directory
RUN rm -rf /GNUstep-build

# Set alias
RUN echo 'alias clang-objc="\${CC} \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -fobjc-arc -lobjc -ldispatch -lgnustep-base"' >> ~/.bashrc

CMD ["/bin/bash"]
```

長いビルド手順でしたね……。最後に `~/.bashrc` に

```sh
alias clang-objc="\${CC} \$(gnustep-config --objc-flags) \$(gnustep-config --objc-libs) -fobjc-arc -lobjc -ldispatch -lgnustep-base"
```

と設定してありますので，この Docker コンテナ内で

```sh
$ clang-objc -o hoge hoge.m
$ ./hoge
```

などとすることで簡単に Objective-C 2.0 のソース `hoge.m` をコンパイル・実行できます。

# Dockerfile およびテストファイル一式

今回作成した Dockerfile およびテスト用 Objective-C ソースの一式は [GitHub レポジトリ](https://github.com/doraTeX/Docker-clang9-objc2) にアップしておきました。

```sh
$ docker run --rm -it -v $(pwd):/workdir doratex/clang9-objc2:latest /bin/bash
# cd test
# ./test_all.sh
```

とすることで，様々な Objective-C 2.0 の機能をテストできます。（ただしテストのうち最後の GUI のテストは X Window System のサーバとつながっていないと動きません。）

# Docker イメージ

今回作成した Docker イメージは [Docker Hub のレポジトリ](https://hub.docker.com/repository/docker/doratex/clang9-objc2) にアップしてあります。

```sh
$ docker pull doratex/clang9-objc2:ubuntu1910
```

などとすれば使えます。