Percival Ulysses
Percival Ulysses

Reputation: 1133

Building an OCaml cross-compiler - configure part

I need to build an OCaml cross-compiler. Sadly, it seems this is not supported out of the box and needs a little work, as described for an older version of the OCaml compiler.
My first question is: What is a nice way to generate the files config/m.h, config/s.h and config/Makefile?

Upvotes: 3

Views: 870

Answers (2)

Percival Ulysses
Percival Ulysses

Reputation: 1133

With a modified configure "chain" it is possible to generate the files. Ocamls configure script assumes that it can compile and execute the results on the same run, which can be impossible in a cross compile environment.
Hence the configure procedure must be modified such that the results of the compilations (including the executables) are stored and can be used in a second run on the target machine. Here is the diff file showing the modification (~200 lines).

diff -r -U 1 ocaml-4.00.0-orig/config/auto-aux/hasgot ocaml-4.00.0-cross/config/auto-aux/hasgot
--- ocaml-4.00.0-orig/config/auto-aux/hasgot
+++ ocaml-4.00.0-cross/config/auto-aux/hasgot
@@ -15,2 +15,4 @@

+. ./keyval.sh
+
 opts=""
@@ -36,7 +38,13 @@

+key="$cc $args"
+getValueExit "$key"
+
 if test "$verbose" = yes; then
   echo "hasgot $args: $cc $opts -o tst hasgot.c $libs" >&2
-  exec $cc $opts -o tst hasgot.c $libs > /dev/null
+  `exec $cc $opts -o tst hasgot.c $libs > /dev/null`
 else
-  exec $cc $opts -o tst hasgot.c $libs > /dev/null 2>/dev/null
+  `exec $cc $opts -o tst hasgot.c $libs > /dev/null 2>/dev/null`
 fi
+res=$?
+setValue "$key" "$res"
+exit "$res"
diff -r -U 1 ocaml-4.00.0-orig/config/auto-aux/hasgot2 ocaml-4.00.0-cross/config/auto-aux/hasgot2
--- ocaml-4.00.0-orig/config/auto-aux/hasgot2
+++ ocaml-4.00.0-cross/config/auto-aux/hasgot2
@@ -15,2 +15,4 @@

+. ./keyval.sh
+
 opts=""
@@ -36,7 +38,13 @@

+key="$cc $args"
+getValueExit "$key"
+
 if test "$verbose" = yes; then
   echo "hasgot2 $args: $cc $opts -o tst hasgot.c $libs" >&2
-  exec $cc $opts -o tst hasgot.c $libs > /dev/null
+  `exec $cc $opts -o tst hasgot.c $libs > /dev/null`
 else
-  exec $cc $opts -o tst hasgot.c $libs > /dev/null 2>/dev/null
+  `exec $cc $opts -o tst hasgot.c $libs > /dev/null 2>/dev/null`
 fi
+res=$?
+setValue "$key" "$res"
+exit "$res"
Only in ocaml-4.00.0-cross/config/auto-aux: keyval.sh
diff -r -U 1 ocaml-4.00.0-orig/config/auto-aux/runtest ocaml-4.00.0-cross/config/auto-aux/runtest
--- ocaml-4.00.0-orig/config/auto-aux/runtest
+++ ocaml-4.00.0-cross/config/auto-aux/runtest
@@ -17,6 +17,30 @@
 echo "runtest: $cc -o tst $* $cclibs" >&2
-$cc -o tst $* $cclibs || exit 100
+stream=/dev/stderr
 else
-$cc -o tst $* $cclibs 2> /dev/null || exit 100
+stream=/dev/null
+#$cc -o tst $* $cclibs 2> /dev/null || exit 100
 fi
+
+key="$* $cclibs"
+
+if test "$crossmode" = cross-cc; then
+   i=`cat ./counter`
+   $cc -o tst"$i" $* $cclibs 2> "$stream" || exit 100
+   echo "$key"'%%#%%'tst"$i" >> ./map_runtest
+   i=`expr $i + 1`
+   echo "$i" > ./counter
+   if test "$*" = sizes.c; then
+       echo "4 4 4 2"
+   fi
+   if test `expr "$*" : '.*tclversion.c'` -ne 0; then
+       echo "8.5"
+   fi
+   exit 0
+fi
+if test "$crossmode" = cross-run; then
+   tst=`awk -v ccargs="$key" 'BEGIN {FS="%%#%%"} $1 == ccargs {print $2}' ./map_runtest`
+   exec ./"$tst"
+fi
+
+$cc -o tst $* $cclibs 2> "$stream" || exit 100
 exec ./tst
diff -r -U 1 ocaml-4.00.0-orig/config/auto-aux/tryassemble ocaml-4.00.0-cross/config/auto-aux/tryassemble
--- ocaml-4.00.0-orig/config/auto-aux/tryassemble
+++ ocaml-4.00.0-cross/config/auto-aux/tryassemble
@@ -1,8 +1,16 @@
 #!/bin/sh
+
+. ./keyval.sh
+
+key="$aspp $*"
+getValueExit "$key"
+
 if test "$verbose" = yes; then
 echo "tryassemble: $aspp -o tst $*" >&2
-$aspp -o tst $* || exit 100
+`$aspp -o tst $* || exit 100`
 else
-$aspp -o tst $* 2> /dev/null || exit 100
+`$aspp -o tst $* 2> /dev/null || exit 100`
 fi
+res=$?
+setValue "$key" "$res"

@@ -11,7 +19,14 @@
 if test "$verbose" = yes; then
+key="$as $*"
+getValueExit "$key"
 echo "tryassemble: $as -o tst $*" >&2
-$as -o tst $* || exit 100
+`$as -o tst $* || exit 100`
 else
-$as -o tst $* 2> /dev/null || exit 100
+`$as -o tst $* 2> /dev/null || exit 100`
 fi
+res=$?
+setValue "$key" "$res"
+exit $res
+else
+exit $res
 fi
diff -r -U 1 ocaml-4.00.0-orig/config/auto-aux/trycompile ocaml-4.00.0-cross/config/auto-aux/trycompile
--- ocaml-4.00.0-orig/config/auto-aux/trycompile
+++ ocaml-4.00.0-cross/config/auto-aux/trycompile
@@ -15,7 +15,15 @@

+. ./keyval.sh
+
+key="$cc $* $cclibs"
+getValueExit "$key"
+
 if test "$verbose" = yes; then
 echo "trycompile: $cc -o tst $* $cclibs" >&2
-$cc -o tst $* $cclibs || exit 100
+`$cc -o tst $* $cclibs || exit 100`
 else
-$cc -o tst $* $cclibs 2> /dev/null || exit 100
+`$cc -o tst $* $cclibs 2> /dev/null || exit 100`
 fi
+res=$?
+setValue "$key" "$res"
+exit $res
diff -r -U 1 ocaml-4.00.0-orig/configure ocaml-4.00.0-cross/configure
--- ocaml-4.00.0-orig/configure
+++ ocaml-4.00.0-cross/configure
@@ -47,2 +47,3 @@
 withcamlp4=camlp4
+crossmode=''

@@ -119,2 +120,4 @@
         withcamlp4="";;
+    -cross|--cross)
+       crossmode="$2"; shift;;
     *) echo "Unknown option \"$1\"." 1>&2; exit 2;;
@@ -158,2 +161,21 @@

+case "$crossmode" in
+   cc)
+       crossmode=cross-cc
+       echo 0 > ./counter
+       rm -f ./map_runtest ./map_hasgot
+       touch ./map_runtest ./map_hasgot;;
+   run)
+       crossmode=cross-run
+       if test ! -e ./map_runtest -o ! -e ./map_hasgot; then
+           echo 'Run with -cross cc first'
+           exit 2
+       fi
+       rm -f ./counter;;
+   none) crossmode=none;;
+   "") crossmode=none ;;
+   *)
+       echo 'Unknown crossmode'>&2
+       exit 2;;
+esac
 # Write options to Makefile
@@ -350,3 +372,3 @@
 cc="$bytecc -O $bytecclinkopts"
-export cc cclibs verbose
+export cc cclibs verbose crossmode

@@ -1647,2 +1669,5 @@

+if test "$crossmode" = cross-run; then
+   rm -f tst* ./map_runtest ./map_hasgot
+fi
 # Print a summary 

The configure script gets a new -cross option. When cc is its argument, it only compiles, when it is run, it only executes the compiled stuff. Intermediate results are stored in config/auto-aux/map_{hasgot,runtest}, mostly using setValue and getValueExit for retrieval, both defined in config/auto-aux/keyval.sh. If one supplies the cross toolchain data with

-cc, -as, -aspp, -partialld, -libs, -dllibs, -dldefs

the Makefile should be usable. Finally the file keyval.sh whose content is not in the diff:

getValueExit()
{
if test "$crossmode" = cross-run; then
    res=`awk -v ccargs="$1" 'BEGIN {FS="%%#%%"} $1 == ccargs {print $2; exit}' ./map_hasgot`
    exit "$res"
fi
}

setValue()
{
if test "$crossmode" = cross-cc; then
    echo "$1"'%%#%%'"$2" >> ./map_hasgot
fi
}

If tk is used, one must modify config/auto-aux/runtest and replace 0.0 with its version number. Further, it may be necessary to modify the file config/auto-aux/solaris-ld if solaris is used as target or host machine.

Upvotes: 1

Jeffrey Scofield
Jeffrey Scofield

Reputation: 66803

I've been building OCaml cross compilers for a few years now. (See my profile for a link to my website.) What I do is build the compiler 1 1/2 times. The first time is for the host (with some settings for the target). The second half build is to build the runtime for the target.

My script for building a cross compiler from OS X to to ARM/iOS is named xarm-build. If you have Subversion, you can get a copy from my public repository:

$ svn cat svn://svn.psellos.com/trunk/ocamlxarm/3.1/xarm-build

Disclaimer: right now, this script just builds the bytecode version of the compiler. I.e., the compiler itself is an OCaml bytecode executable. However it produces native code for the target.

If you try this and have any questions, let me know.

To answer your specific question, if your target system is Unix-like you could try running the configure script on the target to generate config/s.h, config/m.h, and config/Makefile, which as you mention are the critical files. If there's a simulator for your target, you can run configure inside the simulator--this is what I do for iOS. Otherwise you have to figure out reasonable contents yourself. (Maybe run configure on a Unix-like system that's as similar as possible to your target.)

Upvotes: 7

Related Questions