2013-06-27 9 views
12

Quindi ho un enorme progetto C esistente che ho inserito nella directory $PROJECT/jni. Normalmente questo progetto viene eseguito eseguendo uno script configure che crea i Makefile che consente quindi la compilazione del progetto tramite make.Come incorporare il file make esistente con Android NDK

Questo progetto è piuttosto grande e contiene molte directory contenenti file di origine e file di intestazione.

Suppongo che mi manca una comprensione fondamentale qui di come si suppone che Android.mk funzioni. Dovrebbe sostituire il configure e il makefile attualmente utilizzato per compilare il progetto? O dovrei incorporare il makefile generato dal mio script di configurazione nello Android.mk? Gli esempi forniti sono piuttosto banali con solo pochi file sorgente. Il mio elenco jni sembra più:

jni/ 
    folder1/subfolder1 
    folder1/subfolder2 
    folder1/source 
    folder2/source 
    ..... 
    foldern/source 
    configure/ 
    configure/configure.sh 
    Makefile 
    Android.mk 

I makefile generati sono abbastanza ampia (buona quantità di configurazione e c'è uno in ogni directory) quindi sono poco perso su come avvicinarsi a questo.

EDIT:

Il problema principale è che gli esempi forniti con l'NDK sono esempi banali. Hanno 3-5 file sorgente nella directory jni di livello superiore. Il mio problema è che questo è un progetto enorme con una configurazione complessa con 4 cartelle di primo livello ciascuna con molte sottodirectory. Non posso semplicemente spostare il sorgente nella cartella jni ed eseguire il compilatore ndk.

risposta

12

Per rispondere alla tua domanda, sì Android.mkè il sistema di build Android. Google menziona a malapena che la "lingua" di questo file è implementata come GNU rende macro. I documenti vogliono che descriva il tuo progetto in termini di quelle macro. Gestiscono tutti i dettagli sgangherati della compilazione incrociata. Sono abbastanza sicuro che Google abbia adottato questo approccio per migliorare la portabilità in avanti dei file Android.mk man mano che gli strumenti di sviluppo si evolvono.

Il risultato è che (e so che non vorrai sentire questo) la risposta migliore è probabilmente scrivere un NDK Android.mk corretto per il tuo grande progetto da zero.

This article espone le stesse osservazioni Ho effettuato il porting di una libreria di circa 800 file e 300k SLOC. Purtroppo ho bruciato per quasi due settimane la stessa conclusione: la compilazione incrociata causa la mancata riuscita di alcuni script configure (causano file config.h errati). Ho "inventato" praticamente le stesse tecniche che usa nell'articolo. Ma anche dopo aver ottenuto una build pulita, la libreria statica risultante non ha funzionato completamente. Le ore di debug non hanno fornito informazioni utili. [Avvertenza: non sono un esperto di strumenti di configurazione. Probabilmente un guru avrebbe individuato il mio errore. Così va.] Mi ci sono voluti un paio di giorni per creare un pulito Android.mk. La libreria risultante ha eseguito tutti i test la prima volta. Ed è stato portato in modo pulito attraverso diversi giri di strumenti di sviluppo.

Sfortunatamente la creazione di una libreria che utilizza configure senza gli strumenti automatici significa creare il proprio config.h a mano per l'ambiente di destinazione. Questo potrebbe non essere così male come sembra. I sistemi IME tendono a definire molto di più nei loro ambienti configure di quello che effettivamente usano. Ottenere una chiara idea delle dipendenze reali può ripagare lo sforzo noioso durante il futuro refactoring.

La dichiarazione sommaria dall'articolo dice tutto:

Autotool è buono solo su sistemi GNU e di utilizzarlo per la compilazione incrociata può essere davvero noioso, confuso, soggetto a errori o addirittura impossibile. Il metodo descritto qui è un trucco e dovrebbe essere usato a proprio rischio.

Scusa, non ho un suggerimento più positivo.

+0

Sono completamente d'accordo con la tua osservazione. La generazione di 'config.h' da' config.h.in' è impossibile in un ambiente cross-compile. Tuttavia, eseguire la configurazione in modo nativo 'potrebbe possibilmente' funzionare, ma è tutta un'altra bestia :). – Samveen

3

Ecco una soluzione per fare le cose al contrario: la costruzione di sia la libreria esterna che il pacchetto Android dai Makefile standard.

Come prerequisito, è necessario installare tutto il necessario per fare riga di comando Android sviluppo:

  • Una toolchain standalone, consultare la documentazione inclusa nel NDK Android;
  • ant.

La struttura dell'esempio è: una directory per la libreria esterna e una directory per le sorgenti Android allo stesso livello con un Makefile in ogni directory e un livello superiore, Makefile ricorsivo:

Makefile 
mylib/ 
    Makefile 
android/ 
    Makefile 

il mylib/Makefile costruisce una libreria statica:

AR=/path/to/standalone/bin/arm-linux-androideabi-ar 
CC=/path/to/standalone/bin/arm-linux-androideabi-gcc 

libmylib.a: mylib.o 
    $(AR) rcs libmylib.a mylib.o 

mylib.o: mylib.c 
    $(CC) -c mylib.c -o mylib.o 

il android/Makefile sta fornendo regole per costruire il Pacchetto Android:

  • abbiamo bisogno di una dipendenza per copiare mylib quando è modificato;
  • stiamo utilizzando un file jni/ndkmake.c per disporre le chiamate su mylib e fornire materiale specifico per Android;
  • il pacchetto Android dipende dalle origini Java e dalla libreria condivisa.

Il Makefile fornisce due bersaglio: release (il default) e debug di costruire o un pacchetto di rilascio o di un debug uno.

NDK_BUILD=/path/to/ndk-build 
JAVASRC=src/com/example/ndkmake/NdkMake.java 

release: bin/NdkMake-release-unsigned.apk 

debug: bin/NdkMake-debug.apk 

bin/NdkMake-release-unsigned.apk: libs/armeabi/libndkmake.so $(JAVASRC) 
ant release 

bin/NdkMake-debug.apk: libs/armeabi/libndkmake.so $(JAVASRC) 
ant debug 

libs/armeabi/libndkmake.so: jni/ndkmake.c jni/libmylib.a 
$(NDK_BUILD) 

jni/libmylib.a: ../mylib/libmylib.a 
cp ../mylib/libmylib.a jni/libmylib.a 

Il file Android.mk fornisce regole per includere la libreria statica nella build, come precompilati. Abbiamo incluso le intestazioni dalla libreria mylib utilizzando LOCAL_EXPORT_C_INCLUDES.

LOCAL_PATH := $(call my-dir) 

include $(CLEAR_VARS) 
LOCAL_MODULE := ndkmake 
LOCAL_SRC_FILES := ndkmake.c 
LOCAL_STATIC_LIBRARIES := mylib-prebuilt 
include $(BUILD_SHARED_LIBRARY) 

include $(CLEAR_VARS) 
LOCAL_MODULE := mylib-prebuilt 
LOCAL_SRC_FILES := libmylib.a 
LOCAL_EXPORT_C_INCLUDES := ../mylib/ 
include $(PREBUILT_STATIC_LIBRARY) 

Ora abbiamo solo bisogno di un Makefile di livello superiore per costruire le due sottodirectory:

all: libmylib package 

libmylib: 
    cd mylib && $(MAKE) 

package: 
    cd android && $(MAKE) 

Qualsiasi modifica alla biblioteca, alle fonti JNI o ​​alle sorgenti Java innescare una ricostruzione di il pacchetto.

+0

Grazie per il vostro consiglio. Potresti chiarire un paio di punti: 1) Cosa intendi con "aggiungi una regola di costruzione per il materiale NDK" quale regola di costruzione? Intendi i comandi specificati normalmente nei file di Android.mk? 2) Dove posso trovare informazioni su questa toolchain standalone? Grazie! – thatidiotguy

+0

È possibile trovare ulteriori informazioni sui toolchain standalone nella documentazione fornita con NDK, c'è una copia online qui: http://www.kandroid.org/ndk/docs/STANDALONE-TOOLCHAIN.html Come per la regola di compilazione per NDK, Voglio dire mantenere Android.mk, ma eseguire ndk-build dal tuo Makefile (vedi il mio esempio) – Guillaume

+0

Posso collegarmi direttamente agli oggetti c compilati? Ad esempio, se faccio il mio software c in questo momento, produrrà 3 file '.so'. C'è un modo per creare un file C con metodi JNI che si collega agli oggetti nei file della libreria? – thatidiotguy

4

La mia risposta funziona meglio in tandem con la risposta di Gene.

./configure La creazione del file di configurazione si basa sulla compilazione (e possibilmente in esecuzione) di piccoli snippet di codice C per ciascun test. I test di compilazione possono essere testati con successo in un ambiente cross-compile. Tuttavia, i test di compilazione e sono impossibili da eseguire in un ambiente di compilazione incrociata. Il successo di ogni test imposta una variabile corrispondente nel modello config.h.in per creare lo config.h.

Così, per avviare il processo di conversione, è necessario impostare CPP, CC, LD e altri strumenti per il set di cross-compilatore di strumenti (probabilmente quelli del NDK) e quindi eseguire ./configure. Una volta fatto, dovrai correggere lo config.h in modo che corrisponda al tuo ambiente di destinazione. Questo è il passaggio più critico e più soggetto a errori.

Per quanto riguarda lo Android.mk, segue un formato abbastanza vicino a Makefile.am che può essere facilmente convertito in esso. È possibile ignorare lo Makefile.in e lo Makefile, poiché vengono generati dallo Makefile.am.

Per fare un esempio di file (versione 5.11), mi sono imbattuto configurare con le seguenti opzioni,

./configure --host arm-toshiba-linux-androideabi --build x86_64-linux-gnu \ 
      --prefix=/data/local/ host_alias=arm-linux-androideabi \ 
      "CFLAGS=--sysroot=~/ndk/platforms/android-8/arch-arm -Wall -Wextra" \ 
      "CPPFLAGS=--sysroot=~/ndk/platforms/android-8/arch-arm" \ 
      CPP=arm-linux-androideabi-cpp 

Il passo successivo è stato quello di prendere il src/Makefile.am come di seguito:

MAGIC = $(pkgdatadir)/magic 
lib_LTLIBRARIES = libmagic.la 
include_HEADERS = magic.h 

bin_PROGRAMS = file 

AM_CPPFLAGS = -DMAGIC='"$(MAGIC)"' 
AM_CFLAGS = $(CFLAG_VISIBILITY) @[email protected] 

libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \ 
     encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \ 
     funcs.c file.h readelf.h tar.h apptype.c \ 
     file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h 
libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0 
if MINGW 
MINGWLIBS = -lgnurx -lshlwapi 
else 
MINGWLIBS = 
endif 
libmagic_la_LIBADD = $(LTLIBOBJS) $(MINGWLIBS) 

file_SOURCES = file.c 
file_LDADD = libmagic.la 
CLEANFILES = magic.h 
EXTRA_DIST = magic.h.in 
HDR= $(top_srcdir)/src/magic.h.in 
BUILT_SOURCES = magic.h 

magic.h:  ${HDR} 
     sed -e "s/X.YY/$$(echo @[email protected] | tr -d .)/" < ${HDR} > [email protected] 

e creare il Android.mk da questo.

L'ultimo passaggio più importante è stato modificare lo config.h in modo da riflettere accuratamente lo stato del sistema di destinazione. Si tratterà di un processo manuale a cui non posso dare una soluzione alternativa, che consiste principalmente nell'esaminare il configure.log, esaminare le intestazioni e "richiamare" Google. I frutti di questa manodopera sono disponibili on XDA.