[Ecls-list] Cross compiling with asdf

Sylvain Ageneau ageneau at gmail.com
Fri Apr 8 22:19:31 UTC 2011


Hello,

As part of the android port, I wanted to add some existing external
libraries to the cross-compiled ecl. I needed to add them as static
libraries directly linked in with ECL.

My cross compiled ecl doesn't have a compiler or asdf. I looked at the
build-modules function from the "compile.lsp" file which almost did what
I needed except it doesn't allow compiling directly from an asdf system
definition which would be much easier.

I first tried to add a new "cross-compile-op" operation to asdf but I
ran into the problem that the c::builder function insists on creating
and loading fasl files which is a problem because in this case it's
trying to load arm code into a x86 ecl.

I ended up with a solution that works well but is not very elegant.
Still mentioning it in case someone wants to add this functionality
properly (see attached script):
The basic idea is to modify compiler::builder and asdf::compile-file* in
such a way that they create both object files for the host and for the
target platform. FASL are still created and loaded but only for the
host. The resulting cross-compiled library is just a by-product of the
build.

Usage:
(load "cross_compile")

(asdf:make-build :ironclad
                 :type :static-library
                 :move-here t
		 :prologue-code '(provide :ironclad))


Sylvain

-------------- next part --------------
(require 'asdf)

;; (setq *asdf-verbose* t)
;; (setf *load-verbose* t)
;; (setf *compile-verbose* t)

(ext:package-lock "COMMON-LISP" nil)
(ext:package-lock "C" nil)

;; (trace c::builder)
;; (trace c::compile)
;; (trace c::compile-file)
;; (trace c::build-fasl)

(push :cross *features*)


(defpackage :util
  (:use :cl)
  (:export
   :join))

(in-package :util)

(defun join (seq sep)
  "Concatenate the strings in `seq' delimited by the separator `sep'."
  (format nil (concatenate 'string "~{~a~^" sep "~}") seq))


(in-package "COMPILER")


(defmacro with-android-env (body)
  `(let* ((sdk "/opt/android/android-ndk-r5")
	  (sdk-ver "android-9")
	  (toolchain (format nil "~a/toolchains/~a" "/opt/android/android-ndk-r5" "arm-linux-androideabi-4.4.3/prebuilt/linux-x86"))
	  (sysroot (format nil "~a/platforms/~a" "/opt/android/android-ndk-r5" sdk-ver))
	  (compiler::*ecl-include-directory* "/opt/ecl/android/include/")
	  (compiler::*ecl-library-directory* "/opt/ecl/android/lib/")

	  (compiler::*cc* (format nil "~a/bin/arm-linux-androideabi-gcc" toolchain))
	  (compiler::*ld* (format nil "~a/bin/arm-linux-androideabi-gcc" toolchain))
	  (compiler::*ar* (format nil "~a/bin/arm-linux-androideabi-ar" toolchain))
	  (compiler::*ranlib* (format nil "~a/bin/arm-linux-androideabi-ranlib" toolchain))
	  (compiler::*cc-flags* (util:join (list "-g"
						 (format nil "--sysroot=~a" sysroot)
						 "-DANDROID  -DPLATFORM_ANDROID"
						 "-O2 -fPIC -fno-common -D_THREAD_SAFE"
						 "-I/opt/android/android-ndk-r5/platforms/android-9/arch-arm/usr/include"
						 "-I/opt/gmp/android/include")
                                          " "))
	  (compiler::*ld-flags* (util:join (list "-g"
						 (format nil "--sysroot=~a" sysroot))
					   " ")))
     (, at body)))


(setf (symbol-function 'builder-orig) (symbol-function 'builder))

(defun builder (target output-name &rest args &key lisp-files &allow-other-keys)
  (case target
    ((:library :static-library :lib)
     (let ((lisp-files-cross (mapcar #'(lambda (x) (merge-pathnames (format nil "~a-cross" (pathname-name x)) x)) lisp-files))
	   (output-name-cross (merge-pathnames (format nil "~a-cross" (pathname-name output-name)) output-name)))
       (with-android-env
	   (apply #'builder-orig target output-name :lisp-files lisp-files-cross args))
       (rename-file output-name output-name-cross))
     (apply #'builder-orig target output-name :lisp-files lisp-files args))
    (otherwise
     (apply #'builder-orig target output-name args))))

(in-package :asdf)
(require 'cmp)

(defun* compile-file* (input-file &rest keys &key output-file &allow-other-keys)
  (let* ((output-file (or output-file (apply 'compile-file-pathname* input-file keys)))
	 (output-file-cross (merge-pathnames (format nil "~a-cross" (pathname-name output-file)) output-file))
         (tmp-file (tmpize-pathname output-file))
         (tmp-file-cross (tmpize-pathname output-file-cross))
         (status :error))
    (multiple-value-bind (output-truename warnings-p failure-p)
	(compiler::with-android-env
	    (apply 'compiler::compile-file input-file :output-file tmp-file-cross :c-file t keys))
      (cond
        (failure-p
         (setf status *compile-file-failure-behaviour*))
        (warnings-p
         (setf status *compile-file-warnings-behaviour*))
        (t
         (setf status :success)))
      (ecase status
        ((:success :warn :ignore)
         (delete-file-if-exists output-file-cross)
         (when output-truename
           (rename-file output-truename output-file-cross)
           (setf output-truename output-file-cross)))
        (:error
         (delete-file-if-exists output-truename)
         (setf output-truename nil)))
      (values output-truename warnings-p failure-p))

    (multiple-value-bind (output-truename warnings-p failure-p)
        (apply 'compiler::compile-file input-file :output-file tmp-file :c-file t keys)
      (cond
        (failure-p
         (setf status *compile-file-failure-behaviour*))
        (warnings-p
         (setf status *compile-file-warnings-behaviour*))
        (t
         (setf status :success)))
      (ecase status
        ((:success :warn :ignore)
         (delete-file-if-exists output-file)
         (when output-truename
           (rename-file output-truename output-file)
           (setf output-truename output-file)))
        (:error
         (delete-file-if-exists output-truename)
         (setf output-truename nil)))
      (values output-truename warnings-p failure-p))))


More information about the ecl-devel mailing list