Wow, is that a proper Android build system?
authorMark Wooding <mdw@distorted.org.uk>
Tue, 19 Jun 2018 10:44:59 +0000 (11:44 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 19 Jun 2018 10:44:59 +0000 (11:44 +0100)
.gitignore
AndroidManifest.xml [new file with mode: 0644]
Makefile
progress.scala
res/drawable/icon.png [new file with mode: 0644]
res/layout/toy.xml [new file with mode: 0644]
res/values/strings.xml [new file with mode: 0644]
sys.scala
toy-activity.scala [new file with mode: 0644]

index cdb06a1..e3dc5e6 100644 (file)
@@ -1,8 +1,5 @@
 /cls/
-*.d
-*.o
-*.so
-*.stamp
 /out/
 /COPYING
 /auto-version
+debug.keystore
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..85cac4b
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="uk.org.distorted.tripe"
+    android:installLocation="internalOnly">
+  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+  <uses-permission android:name="android.permission.INTERNET"/>
+  <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="23"/>
+
+  <application
+      android:icon="@drawable/icon"
+      android:label="@string/app_name">
+    <activity
+       android:label="@string/app_name"
+       android:name=".ToyActivity">
+      <intent-filter>
+       <action android:name="android.intent.action.MAIN"/>
+       <category android:name="android.intent.category.LAUNCHER"/>
+      </intent-filter>
+    </activity>
+  </application>
+</manifest>
index 5edb29a..0675cd4 100644 (file)
--- a/Makefile
+++ b/Makefile
 
 PACKAGE                         = tripe-android
 VERSION                        := $(shell ./auto-version)
+VSN                     = 1
 
 .SECONDEXPANSION: #sorry
 
 ###--------------------------------------------------------------------------
-### Build parameters.
+### Preliminary magic.
 
-abs_builddir           := $(shell pwd)
+empty                   =
+space                   = $(empty) $(empty)
+comma                   = ,
 
-## Where to put the object files.
-OUTDIR                  = out
+definedp                = $(filter-out undefined,$(origin $1))
+defaulting              = $(if $(call definedp,$1),$($1),$2)
 
-## Native C compiler.
-CC                      = gcc
-CFLAGS                  = -O2 -g -Wall -pedantic -Werror
+all::
 
-## Native linker.
-LD                      = gcc -Wl,-z,defs
-LDFLAGS.so              = -shared
+###--------------------------------------------------------------------------
+### Build parameters.
 
-## External `pkg-config' packages required.
-PKGS                    = mLib catacomb
+abs_srcdir             := $(abspath .)
+hostos                 := $(shell uname -s | tr A-Z a-z)
+hostcpu                        := $(shell uname -m | tr A-Z a-z)
 
-## Java development kit.
-JDKDIR                  = /usr/lib/jvm/default-java
-JDK_PLAT                = linux
-JNI_INCLUDES            = $(JDKDIR)/include $(JDKDIR)/include/$(JDK_PLAT)
+## Of course we have `ccache'.
+CCACHE                 := $(shell \
+       if ccache --version >/dev/null 2>&1; then echo ccache; fi)
 
-## Default arguments for the Java runtime.
-JAVADEFS                =
+## Where to put the object files.
+OUTDIR                  = out
+CONFIGDIR               = $(OUTDIR)/config
 
 ## The Java runtime, for some reason, hardcodes its default for
 ## `java.io.tmpdir', inviting security problems.  If the user has defined a
 ## `TMPDIR', then persuade Java to use it.
-explicit-tmpdir-p       = $(if $(filter-out undefined,$(origin TMPDIR)),t,nil)
+explicit-tmpdir-p       = $(if $(call definedp,TMPDIR),t,nil)
 ifeq ($(explicit-tmpdir-p), t)
   JAVADEFS             += -Djava.io.tmpdir=$(TMPDIR)
 endif
 
 ## Java compiler.
-JAVAC                   = javac $(JAVADEFS)
-JAVAFLAGS               =
+JAVAC                   = javac $(addprefix -J,$(JAVADEFS))
+JAVAFLAGS               = -source 1.6 -target 1.6
 
 ## Scala compiler.
 ##
@@ -78,16 +79,134 @@ ifeq ($(noip-available-p), t)
   NOIP                  = noip
 endif
 ifeq "$(explicit-tmpdir-p),$(noip-available-p)" "t,t"
-  SCALAC                = $(NOIP) fsc $(JAVADEFS)
+  SCALAC                = noip fsc $(JAVADEFS)
+  SCALA                         = noip scala $(JAVADEFS)
   SCALAC_RESET          = $(SCALAC) -reset
 else
   SCALAC                = scalac $(JAVADEFS)
+  SCALA                         = scala $(JAVADEFS) -nc
   SCALAC_RESET          =
 endif
 SCALAFLAGS              = -optimise -feature -deprecation -Xfatal-warnings \
                                -Xlint -Xlint:-package-object-classes \
-                               -Yinline-warnings:false
-SCALA_REPL              = $(NOIP) scala $(JAVADEFS)
+                               -Yinline-warnings:false -target:jvm-1.6 \
+                               -Yno-load-impl-class
+
+## Basic C toolchain flags.
+CFLAGS                  = -O2 -g -Wall
+LDFLAGS                         = -Wl,-z,defs
+
+## Host toolchain.
+FLAVOURS               += host
+ENV.host                =
+CC.host                         = gcc
+CFLAGS.host             = $(CFLAGS) -fPIC
+LD.host                         = $(CC.host)
+LDFLAGS.host            = $(LDFLAGS)
+CONFIG.host             =
+
+## Host JNI machinery.
+$(CONFIGDIR)/jdkdir.mk:
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,CONFIG)java -XshowSettings:properties 2>&1 | \
+       sed -n 's:^  *java\.home *= *\(.*\)/jre$$:JDKDIR = \1:p' >$@.new
+       $(V_AT)mv $@.new $@
+REALCLEANFILES         += $(CONFIGDIR)/jdkdir.mk
+include $(CONFIGDIR)/jdkdir.mk
+
+JDKPLAT                        := $(shell \
+       case $(hostos) in \
+         (darwin) echo macosx ;; \
+         (*) echo $(hostos) ;; \
+       esac)
+CFLAGS.host            += -I$(JDKDIR)/include -I$(JDKDIR)/include/$(JDKPLAT)
+
+## Android SDK location.
+ANDROID_SDKDIR          = /usr/local/android/sdk
+MINAPI                  = 15
+TARGETAPI               = 23
+TOOLVERSION             = 4.9
+
+## Android ABI definitions.
+ANDROID_ABIS           += armeabi
+GNUARCH.armeabi                 = arm-linux-androideabi
+PLATARCH.armeabi        = arm
+CFLAGS.ndk-armeabi      =
+
+ANDROID_ABIS.inhibit   += armeabi-v7a
+GNUARCH.armeabi-v7a     = arm-linux-androideabi
+PLATARCH.armeabi-v7a    = arm
+CFLAGS.ndk-armeabi-v7a  =
+
+ANDROID_ABIS.inhibit   += arm64-v8a
+GNUARCH.arm64-v8a       = aarch64-linux-android
+PLATARCH.arm64-v8a      = arm64
+MINAPI.arm64-v8a        = 21
+
+ANDROID_ABIS           += x86
+TOOLCHAINDIR.x86        = x86
+GNUARCH.x86             = i686-linux-android
+PLATARCH.x86            = x86
+
+ANDROID_ABIS.inhibit   += x86_64
+TOOLCHAINDIR.x86_64     = x86_64
+GNUARCH.x86_64          = x86_64-linux-android
+PLATARCH.x86_64                 = x86_64
+MINAPI.x86_64           = 21
+
+FLAVOURS               += $(ANDROID_ABIS)
+
+## Build variants.
+VARIANTS               += debug
+AAPTFLAGS.debug                 = --debug-mode \
+       --rename-manifest-package uk.org.distorted.tripe-debug
+KEYSTORE.debug          = debug.keystore
+JARSIGNERFLAGS.debug    = -storepass public -keypass public
+
+VARIANTS               += release
+KEYSTORE.release        = release.keystore
+JARSIGNERFLAGS.release  =
+
+## Android NDK location.
+ANDROID_NDKDIR          = $(ANDROID_SDKDIR)/ndk-bundle
+NDK_HOSTARCH            = $(hostos)-$(hostcpu)
+
+## Android NDK toolchains.
+ndk-sysroot             = \
+       $(ANDROID_NDKDIR)/platforms/android-$(call defaulting,MINAPI.$1,$(MINAPI))/arch-$(PLATARCH.$1)
+ndk-toolchain-bin       = \
+       $(ANDROID_NDKDIR)/toolchains/$(call defaulting,TOOLCHAINDIR.$1,$(GNUARCH.$1))-$(TOOLVERSION)/prebuilt/$(NDK_HOSTARCH)/bin
+ENV.ndk                         = env PATH=$(call ndk-toolchain-bin,$1):$$PATH
+CC.ndk                  = $(GNUARCH.$1)-gcc
+LD.ndk                  = $(CC.ndk)
+CFLAGS.ndk              = $(CFLAGS) -fPIC -D__ANDROID_API__=$(MINAPI) \
+       $(CFLAGS.ndk-$1) \
+       --sysroot=$(call ndk-sysroot,$1) \
+       -isystem $(ANDROID_NDKDIR)/sysroot/usr/include \
+       -isystem $(ANDROID_NDKDIR)/sysroot/usr/include/$(GNUARCH.$1)
+LDFLAGS.ndk             = $(LDFLAGS) -pie \
+       --sysroot=$(call ndk-sysroot,$1)
+CONFIG.ndk              = --host=$(GNUARCH.$1)
+
+## Flavour options.
+tool                    = $(call defaulting,$2.$1,$(call $2.ndk,$1))
+
+## Subject name for debug key.
+DEBUGDN                         = CN=Straylight/Edgeware, L=Cambridge, C=GB
+
+## Android libraries.
+ANDROID_JAR             = \
+       $(ANDROID_SDKDIR)/platforms/android-$(MINAPI)/android.jar
+
+## Scala libraries.
+$(CONFIGDIR)/scaladir.mk:
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,CONFIG)$(SCALA) -J-XshowSettings:properties -help 2>&1 | \
+               sed -n 's:^  *scala\.home *= *:SCALADIR = :p' >$@.new
+       $(V_AT)mv $@.new $@
+include $(CONFIGDIR)/scaladir.mk
+REALCLEANFILES         += $(CONFIGDIR)/scaladir.mk
+SCALA_LIB               = $(SCALADIR)/lib/scala-library.jar
 
 ## Silent-rules is on by default.
 V                       = 0
@@ -105,38 +224,16 @@ V_AT                       = $(V_AT_$V)
 V_AT_0                  = @
 
 ###--------------------------------------------------------------------------
-### External native packages.
-
-EXTPREFIX               = $(abs_builddir)/$(OUTDIR)/inst
+### Other handy functions.
 
 join-paths              = $(if $(filter /%,$2),$2,$1/$2)
-ext-srcdir              = $(or $($1_SRCDIR),../$1)
-
-PKG_CONFIG              = PKG_CONFIG_LIBDIR=$(OUTDIR)/inst/lib/pkgconfig \
-                               pkg-config --static
-
-PKGS_CFLAGS            := $(foreach p,$(PKGS),$(shell $(PKG_CONFIG) --cflags $p))
-PKGS_LIBS              := $(foreach p,$(PKGS),$(shell $(PKG_CONFIG) --libs $p))
-
-ALL_CFLAGS              = $(CFLAGS) -fPIC \
-                               $(addprefix -I,$(JNI_INCLUDES)) \
-                               -I$(OUTDIR)/inst/include \
-                               -I$(call ext-srcdir,tripe)/common \
-                               -I$(call ext-srcdir,tripe)/priv \
-                               -I$(call ext-srcdir,tripe)/server \
-                               -I$(OUTDIR)/build/tripe/config \
-                               $(PKGS_CFLAGS)
-
-LIBS                    = $(OUTDIR)/build/tripe/server/libtripe.a \
-                               $(OUTDIR)/build/tripe/priv/libpriv.a \
-                               $(OUTDIR)/build/tripe/common/libcommon.a \
-                               -L$(OUTDIR)/inst/lib $(PKGS_LIBS)
 
-###--------------------------------------------------------------------------
-### Various other tweaks and overrides.
-
-## Hack around https://issues.scala-lang.org/browse/SI-9689
-SCALAFLAGS             += -Yno-load-impl-class
+## Datestamp files.
+STAMPDIR                = $(OUTDIR)/stamp
+stamps                  = $(patsubst %,$(STAMPDIR)/%.$1-stamp,$2)
+stamp-base              = $(patsubst $(STAMPDIR)/%-stamp,%,$1)
+stamp-name              = $(basename $(call stamp-base,$1))
+stamp-type              = $(patsubst .%,%,$(suffix $(call stamp-base,$1)))
 
 ###--------------------------------------------------------------------------
 ### And now we can start building things.
@@ -144,76 +241,200 @@ SCALAFLAGS               += -Yno-load-impl-class
 all::
 .PHONY: all
 
-CLEANFILES              =
 clean::
 .PHONY: clean
 
-###--------------------------------------------------------------------------
-### Native C code.
+realclean:: clean
+.PHONY: realclean
 
-out/%.o: %.c
-       $(call v_tag,CC)mkdir -p $(OUTDIR)/ && \
-               $(CC) -c $(ALL_CFLAGS) -MMD -o$@ $<
+distclean:: realclean
+.PHONY: distclean
 
-ALL_SOURCES             =
-DISTFILES              += $(ALL_SOURCES)
+###--------------------------------------------------------------------------
+### External native packages.
 
-objects                         = $(patsubst %.c,$(OUTDIR)/%$2,$1)
-CLEANFILES             += $(OUTDIR)/*.o $(OUTDIR)/*.d
+## Definitions.
+EXTERNALS              += adns
+adns_CONFIG             = --disable-dynamic
 
-###--------------------------------------------------------------------------
-### Java classes.
+EXTERNALS              += mLib
+mLib_DEPS               = adns
+mLib_CONFIG             = --enable-static --disable-shared --with-adns
 
-## Java and Scala source files turn into multiple `.class' files with
-## unpredictable names.  Rather than try to guess stable outputs for these
-## sources, we make artificial `timestamp' files and uses these in our
-## dependencies.
-CLASSDIR                = $(OUTDIR)/cls
-stamps                  = $(patsubst %,$(OUTDIR)/%.$1-stamp,$2)
-
-clean::; rm -rf $(CLASSDIR)
-CLEANFILES             += $(OUTDIR)/*.class-stamp
-
-## Compiling actual Java code.  Note that this confuses the resident Scala
-## compiler, so we have to reset it here.
-CLSISH                 += java
-$(OUTDIR)/%.class-stamp: %.java
-       $(call v_tag,JAVAC)mkdir -p $(CLASSDIR)/ && \
-               $(JAVAC) -d $(CLASSDIR) -cp $(CLASSDIR) $(JAVAFLAGS) $< && \
-               echo built >$@
-       $(V_AT)$(SCALAC_RESET)
+EXTERNALS              += catacomb
+catacomb_DEPS           = mLib
+catacomb_CONFIG                 = --enable-static --disable-shared
 
-## Compiling Scala code.
-CLSEXT                 += scala
-$(OUTDIR)/%.class-stamp: %.scala
-       $(call v_tag,SCALAC)mkdir -p $(CLASSDIR)/ && \
-               $(SCALAC) -d $(CLASSDIR) -cp $(CLASSDIR) $(SCALAFLAGS) $< && \
-               echo built >$@
+EXTERNALS              += tripe
+tripe_DEPS              = mLib catacomb
+tripe_CONFIG            = --without-wireshark --with-adns --with-tunnel=slip
+
+## Machinery.
+ext-stamps              = $(foreach f,$2,$(call stamps,$f,$1))
+
+ext-srcdir              = $(call defaulting,$1_SRCDIR,../$1)
+ext-builddir            = $(OUTDIR)/build.$1/$2
+ext-prefix              = $(OUTDIR)/inst.$1
+ext-absprefix           = $(abs_srcdir)/$(call ext-prefix,$1)
+
+ext-stamp-srcdir        = $(call ext-srcdir,$(call stamp-name,$1))
+ext-stamp-builddir      = \
+       $(call ext-builddir,$(call stamp-type,$1),$(call stamp-name,$1))
+ext-stamp-absprefix     = $(call ext-absprefix,$(call stamp-type,$1))
+ext-stamp-tool          = $(call tool,$(call stamp-type,$1),$2)
+
+EXTSTAMPS               = $(call ext-stamps,$(EXTERNALS),$(FLAVOURS))
+
+$(EXTSTAMPS): \
+               $(STAMPDIR)/%-stamp: \
+               $$(call ext-stamps, \
+                       $$($$(call stamp-name,$$@)_DEPS), \
+                       $$(call stamp-type,$$@))
+       $(V_AT)mkdir -p $(STAMPDIR)
+       $(V_AT)rm -rf $(call ext-stamp-builddir,$@)
+       $(V_AT)mkdir -p $(call ext-stamp-builddir,$@)
+       cd $(call ext-stamp-builddir,$@) && \
+       $(call ext-stamp-tool,$@,ENV) \
+               $(call join-paths,../../..,$(call ext-stamp-srcdir,$@))/configure \
+               --prefix=$(call ext-stamp-absprefix,$@) \
+               $(call ext-stamp-tool,$@,CONFIG) \
+               $($(call stamp-name,$@)_CONFIG) \
+               CC="$(CCACHE) $(call ext-stamp-tool,$@,CC)" \
+               CFLAGS="$(call ext-stamp-tool,$@,CFLAGS) -I$(call ext-stamp-absprefix,$@)/include" \
+               LD="$(call ext-stamp-tool,$@,LD)" \
+               LDFLAGS="$(call ext-stamp-tool,$@,LDFLAGS) -L$(call ext-stamp-absprefix,$@)/lib" \
+               PKG_CONFIG="pkg-config --static" \
+               PKG_CONFIG_LIBDIR=$(call ext-stamp-absprefix,$@)/lib/pkgconfig
+       $(call ext-stamp-tool,$@,ENV) \
+               $(MAKE) -C$(call ext-stamp-builddir,$@)
+       $(call ext-stamp-tool,$@,ENV) \
+               $(MAKE) -C$(call ext-stamp-builddir,$@) install
+       $(V_AT)touch $@
+
+$(foreach f,$(FLAVOURS),$(foreach e,$(EXTERNALS),clean-$e.$f)): clean-%:
+       rm -f $(STAMPDIR)/$*-stamp
+       rm -rf $(call ext-stamp-builddir,$*)
+.PHONY: $(foreach f,$(FLAVOURS),$(foreach e,$(EXTERNALS),clean-$e.$f))
+$(foreach f,$(FLAVOURS),clean-inst.$f): clean-inst.%:
+       rm -rf $(call ext-prefix,$*)
+.PHONY: $(foreach f,$(FLAVOURS),clean-inst.$f)
+
+$(foreach f,$(FLAVOURS),realclean-$f): realclean-%: \
+               $$(foreach e,$$(EXTERNALS),clean-$$e.$$*) \
+               clean-inst.$$*
+realclean:: $(foreach f,$(FLAVOURS),realclean-$f)
 
 ###--------------------------------------------------------------------------
-### Native-code libraries.
+### Our own native programs.
 
-SHLIBS                 += tripe
-tripe_SOURCES           = jni.c
+## Fetching package configuration.
+PKG_CONFIG              = pkg-config --static
+PKGS                    = catacomb mLib
 
-shlibfile               = $(patsubst %,$(OUTDIR)/lib%.so,$1)
-SHLIBFILES              = $(call shlibfile,$(SHLIBS))
-TARGETS                        += $(SHLIBFILES)
-ALL_SOURCES            += $(foreach l,$(SHLIBS),$($l_SOURCES))
+PCOPT.CFLAGS            = --cflags
+PCOPT.LIBS              = --libs
 
-$(call objects,$(tripe_SOURCES),.o): $(call stamps,ext,tripe)
+pkg-config              = $(shell \
+       env PKG_CONFIG_LIBDIR=$(call ext-prefix,$1)/lib/pkgconfig \
+               $(PKG_CONFIG) $(PCOPT.$3) $2)
 
-$(SHLIBFILES): $(OUTDIR)/lib%.so: $$(call objects,$$($$*_SOURCES),.o)
-       $(call v_tag,LD)$(LD) $(LDFLAGS.so) -o$@ $^ $(LIBS)
+## Definitions.
+APKLIBS                        += libtripe.so
+libtripe.so_SRCS        = jni.c
+libtripe.so_EXTS        = mLib catacomb tripe
+libtripe.so_CFLAGS      = -I$(call ext-prefix,$1)/include \
+                               -I$(call ext-srcdir,tripe)/common \
+                               -I$(call ext-srcdir,tripe)/priv \
+                               -I$(call ext-srcdir,tripe)/server \
+                               -I$(call ext-builddir,$1,tripe)/config \
+                               $(call pkg-config,$1,mLib,CFLAGS) \
+                               $(call pkg-config,$1,catacomb,CFLAGS)
+libtripe.so_LIBS        = $(call ext-builddir,$1,tripe)/server/libtripe.a \
+                               $(call ext-builddir,$1,tripe)/priv/libpriv.a \
+                               $(call ext-builddir,$1,tripe)/common/libcommon.a \
+                               -L$(call ext-prefix,$1)/lib \
+                               $(call pkg-config,$1,catacomb,LIBS) \
+                               $(call pkg-config,$1,mLib,LIBS)
+
+## Machinery for compiling.
+objdir                  = $(OUTDIR)/obj.$1
+objects                         = $(patsubst %.c,$(call objdir,$1)/%$3,$2)
+
+apklib-objects          = $(call objects,$1,$($2_SRCS),$3)
+obj-base                = $(patsubst $(OUTDIR)/obj.%.o,%,$1)
+obj-type                = $(patsubst %/,%,$(dir $(call obj-base,$1)))
+obj-name                = $(notdir $(call obj-base,$1))
+obj-tool                = $(call tool,$(call obj-type,$1),$2)
+
+define obj-rule
+$1_OBJS                        := $$(foreach f,$$(FLAVOURS),\
+                               $$(call objects,$$f,$$($1_SRCS),.o))
+DEPFILES               += $$(foreach f,$$(FLAVOURS),\
+                               $$(call objects,$$f,$$($1_SRCS),.d))
+$$($1_OBJS): $$(OUTDIR)/obj.%.o: \
+               $$$$(call obj-name,$$$$@).c \
+               $$$$(call stamps,$$$$(call obj-type,$$$$@),$$$$($1_EXTS))
+       $$(V_AT)mkdir -p $$(dir $$@)
+       $$(call v_tag,CC)$$(call obj-tool,$$@,ENV) \
+       $$(call obj-tool,$$@,CC) -c -o$$@ -MD \
+               $$(call obj-tool,$$@,CFLAGS) \
+               $$(call $1_CFLAGS,$$(call obj-type,$$@)) \
+               $$<
+endef
+$(foreach a,$(APKLIBS), $(eval $(call obj-rule,$a)))
+
+CLEANFILES             += $(OUTDIR)/obj.*/*.o $(OUTDIR)/obj.*/*.d
+
+## Machinery for linking.
+JNIDIR.host             = $(OUTDIR)
+JNIDIR.ndk              = $(OUTDIR)/pkg/lib/$1
+
+define apklib-rule
+INSTFILES              += $$(call tool,$1,JNIDIR)/$2
+$$(call tool,$1,JNIDIR)/$2: $$(call objects,$$f,$$($2_SRCS),.o)
+       $$(V_AT)mkdir -p $$(dir $$@)
+       $$(call v_tag,LD)$$(call tool,$1,ENV) \
+       $$(call tool,$1,LD) -o$$@ \
+               $$(call tool,$1,LDFLAGS) -shared \
+               $$^ \
+               $$(call $2_LIBS,$1)
+endef
+
+$(foreach f,$(FLAVOURS), \
+$(foreach a,$(APKLIBS), \
+       $(eval $(call apklib-rule,$f,$a))))
+
+CLEANFILES             += $(OUTDIR)/pkg/lib/*/lib*.so $(OUTDIR)/lib*.so
 
 ###--------------------------------------------------------------------------
-### Java classes.
+### Java and Scala building.
+
+JARDIR                  = $(OUTDIR)/jar
+CLASSDIR                = $(OUTDIR)/cls
+
+## External libraries we need to adopt.  Grab them and feed them to `dx'
+## separately, because they take aaaaages and we don't want to have to do
+## this on every incremental build.
+JARS                    = $(SCALA_LIB)
+
+DEXJARS                         =
+define jar-rule
+DEXJARS                        += $$(JARDIR)/$$(notdir $1)
+$$(JARDIR)/$$(notdir $1): $1
+       $$(V_AT)mkdir -p $$(JARDIR)/
+       $$(V_AT)rm -f $$@ $$(basename $$@)-new.jar
+       $$(call v_tag,DX)dx --dex --output=$$(basename $$@)-new.jar $1
+       $$(V_AT)mv $$(basename $$@)-new.jar $$@
+endef
+$(foreach j,$(JARS), $(eval $(call jar-rule,$j)))
+
+REALCLEANFILES         += $(JARDIR)/*.jar
 
 ## Writing things out longhand is tedious.  `CLASSES' is a list of entries of
 ## the form `SRC[:DEP,...]', saying that `SRC.ext', being a source file
-## capable of being built into `.class' files and setting `SRC.stamp', should
-## be so built, and that it depends on other similar sources named DEP having
-## been so built.
+## capable of being built into `.class' files and setting `SRC.class-stamp',
+## should be so built, and that it depends on other similar sources named DEP
+## having been so built.
 CLASSES                        += util
 CLASSES                        += sys:util
 CLASSES                        += admin:sys,util
@@ -221,97 +442,114 @@ CLASSES                  += tar:util
 CLASSES                        += progress:sys,util
 CLASSES                        += keys:progress,tar,sys,util
 CLASSES                        += terminal:progress,sys,util
+CLASSES                        += R
+CLASSES                        += toy-activity:R
+
+## Building class files.
+$(STAMPDIR)/%.class-stamp: %.java
+       $(V_AT)mkdir -p $(CLASSDIR)/
+       $(call v_tag,JAVAC)$(JAVAC) $(JAVAFLAGS) \
+               -bootclasspath $(ANDROID_JAR) \
+               -d $(CLASSDIR) -cp $(CLASSDIR) \
+               $<
+       $(V_AT)$(SCALAC_RESET)
+       $(V_AT)touch $@
+$(STAMPDIR)/%.class-stamp: %.scala
+       $(V_AT)mkdir -p $(CLASSDIR)/
+       $(call v_tag,SCALAC)$(SCALAC) $(SCALAFLAGS) \
+               -javabootclasspath $(ANDROID_JAR) \
+               -bootclasspath $(SCALA_LIB) \
+               -d $(CLASSDIR) -cp $(CLASSDIR) \
+               $<
+       $(V_AT)touch $@
 
 ## Machinery for parsing the `CLASSES' list.
-COMMA                   = ,
 class-name              = $(firstword $(subst :, ,$1))
-class-deps              = $(subst $(COMMA), ,$(word 2,$(subst :, ,$1)))
+class-deps              = $(subst $(comma), ,$(word 2,$(subst :, ,$1)))
 
-CLASS_NAMES             = $(foreach c,$(CLASSES),$(call class-name,$c))
+CLASSNAMES              = $(foreach c,$(CLASSES),$(call class-name,$c))
+CLASSSTAMPS             = $(call stamps,class,$(CLASSNAMES))
 
-all:: $(call stamps,class,$(CLASS_NAMES))
-
-$(foreach c,$(CLASSES),$(eval $(call stamps,class,$(call class-name,$c)): \
+$(foreach c,$(CLASSES), \
+$(eval $(call stamps,class,$(call class-name,$c)): \
        $(call stamps,class,$(call class-deps,$c))))
 
-DISTFILES              += $(foreach c,$(CLASSES),\
-                               $(foreach e,$(CLSEXT),\
-                                 $(wildcard $(call class-name,$c).$e)))
-
-###--------------------------------------------------------------------------
-### External packages.
-
-EXTERNALS              += adns
-adns_CONFIG             = --disable-dynamic
-
-EXTERNALS              += mLib
-mLib_DEPS               = adns
-mLib_CONFIG             = --enable-static --disable-shared --with-adns
-
-EXTERNALS              += catacomb
-catacomb_DEPS           = mLib
-catacomb_CONFIG                 = --enable-static --disable-shared
-
-EXTERNALS              += tripe
-tripe_DEPS              = mLib catacomb
-tripe_CONFIG            = --without-wireshark --with-adns --with-tunnel=slip
-
-all:: $(call stamps,ext,$(EXTERNALS))
-CLEANFILES             += $(OUTDIR)/*.ext-stamp
-clean::; rm -rf $(OUTDIR)/inst $(OUTDIR)/build
-
-$(call stamps,ext,$(EXTERNALS)): \
-               $(OUTDIR)/%.ext-stamp: $$(call stamps,ext,$$($$*_DEPS))
-       $(V_AT)rm -rf $(OUTDIR)/build/$*/
-       $(V_AT)mkdir -p $(OUTDIR)/build/$*/
-       cd $(OUTDIR)/build/$*/ && \
-       $(call join-paths,../../..,$(call ext-srcdir,$*))/configure \
-               --prefix=$(EXTPREFIX) \
-               $($*_CONFIG) \
-               CFLAGS="-O2 -g -fPIC -Wall -I$(EXTPREFIX)/include" \
-               LDFLAGS="-L$(EXTPREFIX)/lib" \
-               PKG_CONFIG="pkg-config --static" \
-               PKG_CONFIG_LIBDIR=$(EXTPREFIX)/lib/pkgconfig
-       $(MAKE) -C$(OUTDIR)/build/$*/
-       $(MAKE) -C$(OUTDIR)/build/$*/ -s install
-       $(V_AT)echo done >$@
+CLEANFILES             += $(CLASSSTAMPS)
+clean::; rm -rf $(OUTDIR)/cls
 
 ###--------------------------------------------------------------------------
-### Distribution arrangements.
-
-DISTFILES              += COPYING
-DISTFILES              += Makefile
-DISTFILES              += auto-version
-
-distdir                         = $(PACKAGE)-$(VERSION)
-DISTTAR                         = $(distdir).tar.gz
-
-distdir:
-       rm -rf $(OUTDIR)/$(distdir)
-       mkdir $(OUTDIR)/$(distdir)/
-       echo $(VERSION) >$(OUTDIR)/$(distdir)/RELEASE
-       set -e; for i in $(DISTFILES); do \
-         case $$i in */*) mkdir -p $(OUTDIR)/$(distdir)/$${i%/*}/ ;; esac; \
-         cp $$i $(OUTDIR)/$(distdir)/; \
-       done
-.PHONY: distdir
-
-dist: distdir
-       set -e; cd $(OUTDIR)/; tar chozf ../$(DISTTAR) $(distdir)
-       rm -rf $(distdir)
-.PHONY: dist
+### Android building machinery.
+
+VPATH                  += $(OUTDIR)/src
+CLEANFILES             += $(OUTDIR)/*.apk
+
+AAPTFLAGS               = \
+       --min-sdk-version $(MINAPI) --target-sdk-version $(TARGETAPI) \
+       --version-name "$(VERSION)" --version-code $(VSN)
+
+$(OUTDIR)/src/R.java: AndroidManifest.xml
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,AAPT)aapt package $(AAPTFLAGS) \
+               -M AndroidManifest.xml -S res/ -I $(ANDROID_JAR) \
+               -J $(dir $@) --generate-dependencies
+CLEANFILES             += $(OUTDIR)/src/R.java $(OUTDIR)/src/R.java.d
+-include $(OUTDIR)/src/R.java.d
+
+BINS                    = catsign key pathmtu
+CLEANFILES             += $(INSTFILES)
+define bin-rule
+INSTFILES              += $$(OUTDIR)/pkg/assets/bin/$1/$2
+$$(OUTDIR)/pkg/assets/bin/$1/$2: $$$$(call ext-stamps,$$$$(EXTERNALS),$1)
+       $$(V_AT)mkdir -p $$(dir $$@)
+       $$(call v_tag,CP)cp $$(call ext-prefix,$1)/bin/$2 $$@
+endef
+$(foreach f,$(FLAVOURS), \
+$(foreach b,$(BINS), \
+       $(eval $(call bin-rule,$f,$b))))
+
+DISTCLEANFILES         += debug.keystore
+debug.keystore:
+       $(call v_tag,KEYTOOL)keytool -genkeypair -alias debug \
+               -keyalg RSA -keysize 3072 \
+               -dname "$(DEBUGDN)" -validity 10000 \
+               -keystore $@ -storetype PKCS12 \
+               -storepass public -keypass public
+
+INSTFILES              += $(OUTDIR)/pkg/classes.dex
+$(OUTDIR)/pkg/classes.dex: $(CLASSSTAMPS) $(DEXJARS)
+       $(V_AT)mkdir -p $(dir $@)
+       $(call v_tag,DX)dx --dex --output=$@ $(CLASSDIR) $(JARDIR)
+
+$(foreach v,$(VARIANTS),$(OUTDIR)/tripe-$v.unsigned.apk): \
+$(OUTDIR)/tripe-%.unsigned.apk: $(INSTFILES)
+       $(call v_tag,AAPT)aapt package -f $(AAPTFLAGS) $(AAPTFLAGS.$*) \
+               -M AndroidManifest.xml -S res/ -I $(ANDROID_JAR) \
+               -F $@ $(OUTDIR)/pkg/
+
+$(foreach v,$(VARIANTS),$(OUTDIR)/tripe-$v.signed.apk): \
+$(OUTDIR)/tripe-%.signed.apk: $(OUTDIR)/tripe-%.unsigned.apk $$(KEYSTORE.$$*)
+       $(call v_tag,JARSIGN)jarsigner -keystore $(KEYSTORE.$*) \
+               $(JARSIGNERFLAGS.$*) \
+               -signedjar $@ $< $(call defaulting,KEYALIAS.$*,$*)
+
+$(foreach v,$(VARIANTS),$(OUTDIR)/tripe-$v.apk): \
+$(OUTDIR)/tripe-%.apk: $(OUTDIR)/tripe-%.signed.apk
+       $(call v_tag,ZIPALGN)zipalign $(ZIPALIGNFLAGS.$*) -f 4 $< $@
+
+$(VARIANTS): %: $(OUTDIR)/tripe-%.apk
+.PHONY: $(VARIANTS)
 
 ###--------------------------------------------------------------------------
 ### Finishing touches.
 
-all:: $(TARGETS)
+all:: debug
 
-clean::; rm -f $(CLEANFILES) $(TARGETS)
+clean::; rm -f $(CLEANFILES)
+realclean::; rm -f $(REALCLEANFILES)
 
-repl: all
-       $(SCALA_REPL) -cp $(CLASSDIR) -Djava.library.path=$(OUTDIR)
-.PHONY: repl
+t:; : $(show)
+.PHONY: t
 
--include $(call objects,$(ALL_SOURCES),.d)
+-include $(DEPFILES)
 
 ###----- That's all, folks --------------------------------------------------
index ab40c5d..308b535 100644 (file)
@@ -90,8 +90,8 @@ trait OperationReporter extends BaseReporter {
   def step(detail: String);
 }
 
-def withReporter[T, R <: BaseReporter]
-       (rep: R, body: R => T): T = {
+def withReporter[T, P <: BaseReporter]
+       (rep: P, body: P => T): T = {
   val ret = try { body(rep) }
   catch { case e: Exception => rep.failed(e); throw e; }
   rep.done();
diff --git a/res/drawable/icon.png b/res/drawable/icon.png
new file mode 100644 (file)
index 0000000..ea933ec
Binary files /dev/null and b/res/drawable/icon.png differ
diff --git a/res/layout/toy.xml b/res/layout/toy.xml
new file mode 100644 (file)
index 0000000..7929429
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+  <TextView
+      android:text="@string/toy_greeting"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_centerInParent="true"/>
+  <Button
+      android:text="@string/toy_ok"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_alignParentRight="true"
+      android:layout_alignParentBottom="true"
+      android:onClick="clickOk"/>
+</RelativeLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644 (file)
index 0000000..ea80ce2
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+  <string name="app_name">TrIPE</string>
+  <string name="toy_greeting">Hello, world!</string>
+  <string name="toy_ok">OK</string>
+</resources>
index 402bf1d..8dab4c6 100644 (file)
--- a/sys.scala
+++ b/sys.scala
@@ -773,8 +773,9 @@ def runCommand(cmd: String*): (String, String) = {
   withCleaner { clean =>
 
     /* Create the child process and pick up the ends of its streams. */
-    val pb = new ProcessBuilder(cmd.asJava).redirectInput(devnull);
+    val pb = new ProcessBuilder(cmd.asJava);
     val kid = pb.start(); clean { kid.destroy(); }
+    kid.getOutputStream.close();
     val out = kid.getInputStream(); clean { out.close(); }
     val err = kid.getErrorStream(); clean { err.close(); }
 
diff --git a/toy-activity.scala b/toy-activity.scala
new file mode 100644 (file)
index 0000000..868b3d2
--- /dev/null
@@ -0,0 +1,93 @@
+package uk.org.distorted.tripe;
+
+import java.io.{File, FileOutputStream, InputStream, IOException};
+
+import android.app.{Activity, Application};
+import android.content.Context; import Context.MODE_WORLD_READABLE;
+import android.content.res.AssetManager;
+import android.os.Build; import Build.{CPU_ABI, CPU_ABI2};
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+
+import scala.util.control.Breaks;
+
+object Setup {
+  private final val TAG = "Setup";
+  private val BREAK = new Breaks;
+  import BREAK.{breakable, break};
+
+  def setup(ctx: Context) {
+    val bindir = ctx.getDir("bin", MODE_WORLD_READABLE);
+    val assets = ctx.getAssets;
+
+    val abis =
+      try { classOf[Build].getField("SUPPORTED_ABIS").get(null).asInstanceOf[Array[String]] }
+      catch {
+       case _: NoSuchFieldException => Array(CPU_ABI, CPU_ABI2) flatMap {
+         case null | "" => None
+         case s => Some(s)
+       }
+      };
+
+    Log.d(TAG, s"abis = ${abis.mkString(", ")}");
+    Log.d(TAG, s"assets: ${assets.list("bin").mkString(", ")}");
+
+    for (abi <- abis) {
+      val binsrc = s"bin/$abi";
+      for (base <- assets.list(binsrc)) {
+       val prog = new File(bindir, base);
+       if (!prog.exists) try {
+         Log.d(TAG, s"creating $prog...");
+         val in = assets.open(s"$binsrc/$base");
+         Log.d(TAG, "opened source...");
+         val out = new FileOutputStream(prog);
+         Log.d(TAG, "opened target...");
+         val buf = new Array[Byte](4096);
+         breakable {
+           while (true) {
+             val n = in.read(buf);
+             Log.d(TAG, s"read $n bytes...");
+             if (n <= 0) break;
+             out.write(buf, 0, n);
+           }
+         }
+         in.close();
+         out.close();
+         Log.d(TAG, "set permissions...");
+         if (!prog.setReadable(true, false) ||
+             !prog.setExecutable(true, false))
+           throw new IOException("failed to set program permissions");
+       } catch {
+         case exc: IOException =>
+           Log.wtf(TAG, "fuck, failed to create prog", exc);
+       }
+      }
+    }
+    Log.d(TAG, "all OK");
+  }
+}
+
+object ToyActivity {
+  private final val TAG = "ToyActivity";
+  System.loadLibrary("jni");
+  @native protected def foo();
+}
+
+class ToyActivity extends Activity {
+  import ToyActivity._;
+
+  override protected def onCreate(joy: Bundle) {
+    super.onCreate(joy);
+    Setup.setup(this);
+    setContentView(R.layout.toy);
+  }
+  def clickOk(v: View) {
+    Log.d(TAG, "OK, OK.  (Scala was here.)");
+    foo();
+
+    val bindir = getDir("bin", MODE_WORLD_READABLE);
+    Runtime.getRuntime.exec(Array(new File(bindir, "prog").getPath,
+                                 "testing", "1", "2", "3"));
+  }
+}