From this answer I found that my problem might have been that I was not using a "modern" Objective-C runtime.
Instance variable declarations inside the @implementation { }
block is
a relatively recent Objective-C feature. . . . you also need to be
eligible for the "Modern" Objective-C runtime . . .
From this thread I found,
Providing -fobjc-nonfragile-abi will also make some changes to the
runtime. On Darwin it will use Apple's 'Modern' runtime, while not
specifying it will generate code for the 'Legacy' runtime. If you
specify -fobjc-nonfragile-abi and -fgnu-runtime, then clang will
generate code for the GNUstep runtime, which you can find in GNUstep
svn as libobjc2. This provides all of the ObjC 2 features except for
GC, and a few others not found in the Mac runtimes.
So, I tried adding the following to my GNUmakefile:
Main_OBJCFLAGS = -fobjc-nonfragile-abi
But when I tried to compile, I got the error:
fatal error: 'objc/blocks_runtime.h' file not found
From there, I found this thread and concluded that I probably needed "libobjc2".
Disappointingly, I couldn't find a "libobjc2" package for Debian, so I attempted to compile it from source. First I had to install some build dependencies:
sudo apt-get install cmake llvm-dev
Then I had to apply this patch to the llvm-3.5-dev package, because it was causing the libobjc2 compilation to fail:
wget -qO- https://launchpadlibrarian.net/221945609/fix-llvm-3.5-dev-debian.bash | bash
So then I was able to compile libobjc2 with the following commands:
wget http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2
tar jxf libobjc2-1.7.tar.bz2
cd libobjc2-1.7
mkdir Build
cd Build
CC=clang CXX=clang++ cmake -DCMAKE_INSTALL_LIBDIR=lib ..
make -j8
sudo -E make install
After that, I tried to compile the code from the tutorial again. Compilation succeeded (without making any of the changes that I didn't want to make)! However, make complained about a "potential conflict" between one of my ".so" files (I can't recall the exact error), and when I ran my program (with ./obj/Main
), I saw the following in the program output:
$ ./obj/Main
Loading two versions of Protocol. The class that will be used is undefined
Loading two versions of Object. The class that will be used is undefined
2016-11-19 16:43:24.012 Main[10166] Created a Toyota Corolla
2016-11-19 16:43:24.017 Main[10166] Changed the car to a Toyota Camry
2016-11-19 16:43:24.017 Main[10166] Driving a Toyota Camry. Vrooom!
Apparently, compiling libobjc2 and letting it live alongside the Debian GNUstep packages may not have been "good." So, I took the advice of various online sources and set out to compile GNUstep from source.
First I uninstalled the Debian packages:
sudo apt-get uninstall gnustep-devel && sudo apt-get autoremove
Then I installed the following build dependencies:
sudo apt-get install libffi-dev libicu-dev libgnutls28-dev libxml2-dev libxslt1-dev libtiff5-dev libjpeg-dev libpng-dev libgif-dev libaspell-dev libcups2-dev libaudiofile-dev portaudio19-dev libdispatch-dev
(I can't guarantee that's the complete list of dependencies, only the ones I could find documented; I may have had a few more necessary libraries already installed on my computer as a result compiling unrelated programs.)
Using the following two tutorials (1) (2), adapting them slightly (perhaps unnecessarily), I managed to compile GNUstep.
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.8.tar.gz | tar xz
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.9.tar.gz | tar xz
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.25.0.tar.gz | tar xz
wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.25.0.tar.gz | tar xz
cd gnustep-make-2.6.8
CC=clang ./configure --enable-objc-nonfragile-abi
sudo make install
cd ..
cd gnustep-base-1.24.9
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make
sudo make install
cd ..
cd gnustep-gui-0.25.0
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make
sudo make install
cd ..
cd gnustep-back-0.25.0
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" ./configure
CC=clang OBJCFLAGS="-fblocks -fobjc-nonfragile-abi" make
sudo make install
cd ..
After that, I compiled my tutorial files again, this time with:
. /usr/local/share/GNUstep/Makefiles/GNUstep.sh && make -k
At last, I no longer get an error about any "conflict", and when I run the program, I no longer see the errors of the form "Loading two versions of . . ."
$ ./obj/Main
2016-11-19 19:56:07.450 Main[10822:10822] Created a Toyota Corolla
2016-11-19 19:56:07.451 Main[10822:10822] Changed the car to a Toyota Camry
2016-11-19 19:56:07.451 Main[10822:10822] Driving a Toyota Camry. Vrooom!
Success!
I've also refactored this solution slightly, into a script. That might be a little "cleaner" and easier to use. I'll paste the latest version here "just in case:"
#!/usr/bin/env bash
# It seems that many modern Objective-C features aren't available without
# libobjc2, which doesn't seem to be available as a Debian package. Also,
# compiling and using it alongside the Debian GNUstep packages doesn't work too
# well (it seems like they may each provide their own definition of the Protocol
# and Object classes). Basically, to get a fully-functioning Objective-C
# compilation environment on Debian 8, run this script.
# Please ensure any Debian GNUstep packages are uninstalled before running this
# script.
# Slightly adapted from
# http://wiki.gnustep.org/index.php/GNUstep_under_Ubuntu_Linux for Debian 8.
# Also, uses the latest stable versions of source packages as of this writing
# (hopefully to improve reproducability of success; but feel free to upgrade
# them if you want).
# If this script is successful, you should be able to compile a "main.m" program
# by running "make" in a directory with a "GNUmakefile" with these contents:
#
# include $(GNUSTEP_MAKEFILES)/common.make
# TOOL_NAME = Main
# Main_OBJC_FILES = main.m
# include $(GNUSTEP_MAKEFILES)/tool.make
# Show prompt function
function showPrompt()
{
if [ "$PROMPT" = true ] ; then
echo -e "\n\n"
read -p "${GREEN}Press enter to continue...${NC}"
fi
}
# Set colors
GREEN=`tput setaf 2`
NC=`tput sgr0` # No Color
# Set to true to pause after each build to verify successful build and installation
PROMPT=true
# Install Requirements
sudo apt update
echo -e "\n\n${GREEN}Installing dependencies...${NC}"
sudo apt -y install clang ninja cmake libffi-dev libxml2-dev \
libgnutls28-dev libicu-dev libblocksruntime-dev libkqueue-dev libpthread-workqueue-dev autoconf libtool \
libjpeg-dev libtiff5-dev libffi-dev libcairo2-dev libx11-dev libxt-dev libxft-dev \
llvm-dev libdispatch-dev
# https://bugs.launchpad.net/ubuntu/+source/llvm/+bug/1387011/comments/17
wget -qO- https://launchpadlibrarian.net/221945609/fix-llvm-3.5-dev-debian.bash | bash
# Create build directory
mkdir GNUstep-build
cd GNUstep-build
# Set clang as compiler
export CC=clang
export CXX=clang++
# Checkout sources
echo -e "\n\n${GREEN}Downloading sources...${NC}"
mkdir -p libobjc2 && wget -qO- https://github.com/gnustep/libobjc2/archive/v1.8.1.tar.gz | tar xz -C libobjc2 --strip-components=1
mkdir -p make && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.8.tar.gz | tar xz -C make --strip-components=1
mkdir -p base && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.9.tar.gz | tar xz -C base --strip-components=1
mkdir -p gui && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.25.0.tar.gz | tar xz -C gui --strip-components=1
mkdir -p back && wget -qO- ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.25.0.tar.gz | tar xz -C back --strip-components=1
showPrompt
# Build GNUstep make first time
echo -e "\n\n"
echo -e "${GREEN}Building GNUstep-make for the first time...${NC}"
cd make
./configure --enable-debug-by-default --with-layout=gnustep --enable-objc-nonfragile-abi --enable-objc-arc
make -j8
sudo -E make install
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
echo ". /usr/GNUstep/System/Library/Makefiles/GNUstep.sh" >> ~/.bashrc
# showPrompt
# Build libobjc2
echo -e "\n\n"
echo -e "${GREEN}Building libobjc2...${NC}"
cd ../libobjc2
rm -Rf build
mkdir build && cd build
cmake ../ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang -DCMAKE_ASM_COMPILER=clang -DTESTS=OFF
cmake --build .
sudo -E make install
sudo ldconfig
export LDFLAGS=-ldispatch
showPrompt
OBJCFLAGS="-fblocks -fobjc-runtime=gnustep-1.8.1"
# Build GNUstep make second time
echo -e "\n\n"
echo -e "${GREEN}Building GNUstep-make for the second time...${NC}"
cd ../../make
./configure --enable-debug-by-default --with-layout=gnustep --enable-objc-nonfragile-abi --enable-objc-arc
make -j8
sudo -E make install
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
showPrompt
# Build GNUstep base
echo -e "\n\n"
echo -e "${GREEN}Building GNUstep-base...${NC}"
cd ../base/
./configure
make -j8
sudo -E make install
showPrompt
# Build GNUstep GUI
echo -e "\n\n"
echo -e "${GREEN} Building GNUstep-gui...${NC}"
cd ../gui
./configure
make -j8
sudo -E make install
showPrompt
# Build GNUstep back
echo -e "\n\n"
echo -e "${GREEN}Building GNUstep-back...${NC}"
cd ../back
./configure
make -j8
sudo -E make install
showPrompt
. /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
echo -e "\n\n"
echo -e "${GREEN}Install is done. Open a new terminal to start using.${NC}"