2012-12-19 12 views
7

Próbuję uzyskać SCons, aby wygenerować wiele celów (numer nieznany bezpośrednio w SConscript).SCons generuje zmienną liczbę celów

Mam katalogu jak:

headers/ 
    Header1.h 
    Header2.h 
    Header3.h 
    Header4.h 
meta/ 
    headers_list.txt 

Teraz chcę SConscript czytać headers_list.txt, w oparciu o jego zawartość odebrać pliki z headers/ katalogu (czyli może on zawierać tylko Header1 i Header3), dla każdej z tych Chcę generować źródło za pomocą jakiejś funkcji.

I zostały próbuje użyć env.Command to zrobić, ale problemem jest to, że wymaga rozmówcy określić listę celów, które z oczywistych względów nie wiadomo, kiedy powołując env.Command.

Jedyne co mogę myśleć jest uruchomiony:

for header in parse(headers_file): 
    source = mangle_source_name_for_header(header) 
    env.Command(source, header, generator_action) 

Ale oznacza to będę uruchomiony parse(headers_file) każdym razem modlę scons. Jeśli parsowanie jest kosztowne, a plik nie jest często zmieniany, krok ten można łatwo buforować.

Co SConsc konstrukcja/klasa/technika brakuje mi do osiągnięcia tego buforowania?

edit:

Wydaje się moje pytanie jest podobna do Build-time determination of SCons targets, ale nie ma to technika bez sztucznej pliku manekina?

Ponadto, nawet z plikiem tymczasowym, nie widzę, jak mam przekazać target zmienną z Command, która generuje zmienną liczbę celów do drugiej, która mogłaby iterować nad nimi.

edit 2:

This wygląda obiecująco.

Odpowiedz

3

Jedyny sposób, w jaki udało mi się to zrobić, to emitter. Poniżej przykład składa się z 3 plików:

./ 
|-SConstruct 
|-src/ 
| |-SConscript 
| |-source.txt 
|-build/ 

SConstruct

env = Environment() 

dirname = 'build' 
VariantDir(dirname, 'src', duplicate=0) 

Export('env') 

SConscript(dirname+'/SConscript') 

src/SConscript

Import('env') 

def my_emitter(env, target, source): 
    data = str(source[0]) 
    target = [] 
    with open(data, 'r') as lines: 
     for line in lines: 
      line = line.strip() 
      name, contents = line.split(' ', 1) 
      if not name: continue 

      generated_source = env.Command(name, [], 'echo "{0}" > $TARGET'.format(contents)) 
      source.extend(generated_source) 
      target.append(name+'.c') 

    return target, source 

def my_action(env, target, source): 
    for t,s in zip(target, source[1:]): 
     with open(t.abspath, 'w') as tf: 
      with open(s.abspath, 'r') as sf: 
       tf.write(sf.read()) 

SourcesGenerator = env.Builder(action = my_action, emitter = my_emitter) 
generated_sources = SourcesGenerator(env, source = 'source.txt') 

lib = env.Library('functions', generated_sources) 

src/source.txt

a int a(){} 
b int b(){} 
c int c(){} 
d int d(){} 
g int g(){} 

Wyjście:

$ scons 
scons: Reading SConscript files ... 
scons: done reading SConscript files. 
scons: Building targets ... 
echo "int a(){}" > build/a 
echo "int b(){}" > build/b 
echo "int c(){}" > build/c 
echo "int d(){}" > build/d 
echo "int g(){}" > build/g 
my_action(["build/a.c", "build/b.c", "build/c.c", "build/d.c", "build/g.c"], ["src/source.txt", "build/a", "build/b", "build/c", "build/d", "build/g"]) 
gcc -o build/a.o -c build/a.c 
gcc -o build/b.o -c build/b.c 
gcc -o build/c.o -c build/c.c 
gcc -o build/d.o -c build/d.c 
gcc -o build/g.o -c build/g.c 
ar rc build/libfunctions.a build/a.o build/b.o build/c.o build/d.o build/g.o 
ranlib build/libfunctions.a 
scons: done building targets. 

Również ten ma jedno naprawdę nie podoba, który jest parsowanie headers_list.txt z każdym wykonaniu scons. Czuję, że powinien istnieć sposób na przeanalizowanie go tylko wtedy, gdy plik się zmieni. Mógłbym buforować to ręcznie, ale wciąż mam nadzieję, że jest jakaś sztuczka, która sprawi, że SCONS zajmie się dla mnie buforowaniem.

Nie mogłem znaleźć sposobu, aby nie powielać plików (a i a.c są takie same). Jednym ze sposobów byłoby po prostu wygenerowanie biblioteki w my_action zamiast w źródłach (to podejście zastosowałem w moim ostatecznym rozwiązaniu).

Powiązane problemy