2011-07-28 16 views
7

Czy istnieje najlepsza praktyka dla obsługi zależności od flag preprocesora C/C++, takich jak -DCOMPILE_WITHOUT_FOO? Oto mój problem:Najlepsza praktyka zależności od #defines?

> setenv COMPILE_WITHOUT_FOO 
> make <Make system reads environment, sets -DCOMPILE_WITHOUT_FOO> 
    <Compiles nothing, since no source file has changed> 

Co chciałbym zrobić, to mieć wszystkie pliki, które opierają się na #ifdef sprawozdania uzyskać zrekompilowane:

> setenv COMPILE_WITHOUT_FOO 
> make 
    g++ FileWithIfdefFoo.cpp 

Co mi się nie chce, to mieć przekompilować wszystko, jeśli wartość COMPILE_WITHOUT_FOO nie zmienił się.

Mam prymitywny skrypt w języku Python działający (patrz poniżej), który zasadniczo zapisuje plik nagłówkowy FooDefines.h, a następnie porównuje go, aby zobaczyć, czy coś jest inne. Jeśli tak jest, zastępuje on FooDefines.h, a następnie przejmuje konwencjonalna zależność od pliku źródłowego. Zdefiniowany jest , a nie przekazany w linii poleceń z -D. Wadą jest to, że muszę teraz dołączyć FooDefines.h w dowolnym pliku źródłowym, który używa #ifdef, a także mam nowy, dynamicznie generowany plik nagłówkowy dla każdego #ifdef. Jeśli istnieje narzędzie do tego, lub sposób, aby uniknąć korzystania z preprocesora, jestem uszy.

import os, sys 
def makeDefineFile(filename, text): 
    tmpDefineFile = "/tmp/%s%s"%(os.getenv("USER"),filename) #Use os.tempnam? 
    existingDefineFile = filename 

    output = open(tmpDefineFile,'w') 
    output.write(text) 
    output.close() 

    status = os.system("diff -q %s %s"%(tmpDefineFile, existingDefineFile)) 

    def checkStatus(status): 
     failed = False 
     if os.WIFEXITED(status): 
      #Check return code 
      returnCode = os.WEXITSTATUS(status) 
      failed = returnCode != 0 
     else: 
      #Caught a signal, coredump, etc. 
      failed = True 
     return failed,status 

    #If we failed for any reason (file didn't exist, different, etc.) 
    if checkStatus(status)[0]: 
     #Copy our tmp into the new file 
     status = os.system("cp %s %s"%(tmpDefineFile, existingDefineFile)) 
     failed,status = checkStatus(status) 
     print failed, status 
     if failed: 
      print "ERROR: Could not update define in makeDefine.py" 
      sys.exit(status) 

Odpowiedz

0

Wyzwalacze na znacznikach daty na plikach. Plik zależny, który jest nowszy od tego, co zależy od niego, powoduje jego rekompilację. Będziesz musiał umieścić swoją definicję dla każdej opcji w osobnym pliku .h i upewnić się, że te zależności są reprezentowane w pliku Makefile. Następnie, jeśli zmienisz opcję, pliki zależne od niej zostaną automatycznie skompilowane ponownie.

Jeśli uwzględnia pliki zawierające pliki, nie będzie konieczna zmiana struktury źródła. Możesz dołączyć plik "BuildSettings.h", który zawiera wszystkie indywidualne pliki ustawień.

Jedynym trudnym problemem byłoby, gdybyś był na tyle sprytny, aby przeanalizować kod include guards. Widziałem problemy z kompilacją, ponieważ obejmują kolizje nazw plików i kolejność przeszukiwania katalogów włączających.

Teraz, gdy o tym wspomnisz, powinienem sprawdzić i sprawdzić, czy mój IDE jest wystarczająco inteligentny, aby automatycznie utworzyć dla mnie te zależności. Brzmi jak wspaniała rzecz do dodania do IDE.

+0

dodał raport o błędzie dla używanego IDE. Przy odrobinie szczęścia mogą to naprawić wkrótce – Jay

2

Nie wierzę, że można określić automagicznie. Wytyczne preprocesora nie są kompilowane w nic. Ogólnie rzecz biorąc, oczekuję przeprowadzenia pełnej rekompilacji, jeśli zależę od definicji. DEBUG jest znanym przykładem.

Nie sądzę, że jest taki sposób na wykonanie prawa. Jeśli nie możesz tego zrobić we właściwy sposób, najgłupszy możliwy sposób to prawdopodobnie najlepsza opcja. Przeszukiwanie tekstu dla COMPILE_WITH_FOO i tworzenie zależności w ten sposób. Chciałbym to sklasyfikować jako shenanigana i jeśli piszesz wspólny kod, poleciłbym uzyskać całkiem znaczące zakupy od współpracowników.

CMake ma kilka udogodnień, które mogą ułatwić to. W tym celu utworzysz niestandardowy cel. Możesz jednak wymieniać tutaj problemy, zachowując listę plików zależnych od Twojego symbolu. Twoje wyszukiwanie tekstu może wygenerować ten plik, jeśli zostanie zmieniony. Użyłem podobnych technik, sprawdzając, czy muszę odbudować repozytoria danych statycznych na podstawie znaczników czasu wget.

Cheetah to kolejne narzędzie, które może być przydatne.

Jeśli to ja, to myślę, że będę wykonywał pełne przebudowy.

+0

Niestety, myślę, że masz rację. W tym zespole nie byłoby problemu z wymuszeniem użycia mechanizmu (zwłaszcza, że ​​zbudowałem nasz system make [na szczycie QMake]). Jednak nie ma standardowego, a nawet preferowanego sposobu, aby to zrobić. Jeszcze jeden powód, dla którego preprocesor powinien być używany tylko wtedy, gdy jest absolutnie niezbędny. –

+0

Kompilacja warunkowa jest zawsze żmudna. Używanie go oszczędnie z kompilacjami codziennymi/integracyjnymi to najmniej opresyjne rozwiązanie, jakie widziałem w tej sprawie. Ograniczeniem jest to, że większość organizacji nie jest skonfigurowana do tego w prywatnych oddziałach. –

3

To z pewnością nie jest to najładniejszy podejście, ale to działa:

find . -name '*cpp' -o -name '*h' -exec grep -l COMPILE_WITHOUT_FOO {} \; | xargs touch 

To będzie przejrzeć kodzie źródłowym dla makra COMPILE_WITHOUT_FOO, i „dotknąć” każdy plik, który będzie aktualizować sygnatury czasowej. Następnie, gdy uruchomisz make, pliki te będą rekompilowane.

Jeśli zainstalowano ack można uprościć polecenia:

ack -l --cpp COMPILE_WITHOUT_FOO | xargs touch 
+0

To interesujące podejście. Myślę, że grep jest wystarczający, ponieważ zależności są raczej płaskie, niż zależne od siebie. Na nieszczęście dla mnie mam Perforce, która pozostawia pliki w stanie tylko do odczytu, chyba że są one wyrejestrowane. Gdybym używał gita, twoje rozwiązanie byłoby idealne. –

+0

Boo for perforce – ojblass

+0

@Walter - Używam też Perforce. Powinieneś być w stanie sprawdzić cały katalog, który pozwoliłby ci na użycie tego. Ale kiedy przejdziesz do sprawdzania zmian, pamiętaj, aby najpierw wykonać "Przywróć niezmienione pliki". – Tim

0

Jay zwrócił uwagę, że „make wyzwalaczy na znaczniki czasu data na plikach”.

Teoretycznie możesz mieć swój główny plik makefile, nazwać go m1, dołączyć zmienne z drugiego pliku makefile o nazwie m2. m2 zawierałoby listę wszystkich flag preprocesora.

Możesz mieć regułę make dla swojego programu, w zależności od tego, czy m2 jest aktualne.

Reguła dla m2 spowoduje zaimportowanie wszystkich zmiennych środowiskowych (a więc dyrektyw #include).

lewę, reguła do zrobienia m2 wykryłaby, czy istnieje różnica z poprzedniej wersji. Jeśli tak, pozwoliłoby to na zmienną, która wymusiłaby "make all" i/lub oczyść dla głównego celu. w przeciwnym razie wystarczyłoby zaktualizować znacznik czasu na m2 i nie uruchamiać pełnego remake'u.

Wreszcie, reguła dla normalnego celu (make all) będzie źródłem w dyrektywach preprocesora od m2 i zastosuj je zgodnie z wymaganiami.

To brzmi łatwo/jest możliwe w teorii, ale w praktyce GNU Make jest znacznie trudniejsze do uzyskania tego typu rzeczy do pracy. Jestem pewien, że można to zrobić.

+0

To naprawdę całkiem proste. "include cppflagsfile" i "$ (ENVDEPOBJS): cppflagsfile" już działają. – thiton

1

Twój problem wydaje się być na miarę, aby traktować go z autoconf i autoheader, zapisując wartości zmiennych w pliku config.h. Jeśli nie jest to możliwe, rozważ przeczytanie dyrektyw "-D" z pliku i zapisanie flag do tego pliku.

W każdych okolicznościach należy unikać kompilacji zależnych wyłącznie od zmiennych środowiskowych. Nie masz możliwości powiedzieć, kiedy zmieniło się środowisko. Konieczne jest przechowywanie zmiennych w pliku, najczystszym sposobem byłby autoconf, autoheader oraz źródło i wiele drzew kompilacji; drugi - najczystszy sposób, dla każdego przełącznika kontekstu kompilacji; a trzeci - najczystszy sposób - plik zawierający wszystkie przełączalne przełączniki kompilatora, od których zależą wszystkie obiekty zależne od tych przełączników.

Jeśli zdecydujesz się na zastosowanie trzeciej metody, pamiętaj, aby nie aktualizować tego pliku niepotrzebnie, np. budując go w tymczasowej lokalizacji i kopiując go warunkowo na diff, a następnie sprawi, że reguły będą mogły warunkowo odbudować twoje pliki w zależności od flag.

1

Jednym ze sposobów, aby to zrobić, jest zapisanie każdej poprzedniej wartości # define w pliku i użycie warunkowych w makefile, aby wymusić aktualizację tego pliku, gdy bieżąca wartość nie pasuje do poprzedniej. Wszystkie pliki zależne od tego makra będą zawierać plik jako zależność.

Oto przykład.Aktualizacja zostanie zmieniona na file.o, jeśli zmieniono file.c lub zmienna COMPILE_WITHOUT_FOO różni się od ostatniej. Używa ona $(shell) do porównania bieżącej wartości z wartością zapisaną w pliku envvars/COMPILE_WITHOUT_FOO. Jeśli są różne, tworzy polecenie dla tego pliku, które zależy od force, która jest zawsze aktualizowana.

file.o: file.c envvars/COMPILE_WITHOUT_FOO 
    gcc -DCOMPILE_WITHOUT_FOO=$(COMPILE_WITHOUT_FOO) $< -o [email protected] 

ifneq ($(strip $(shell cat envvars/COMPILE_WITHOUT_FOO 2> /dev/null)), $(strip $(COMPILE_WITHOUT_FOO))) 
force: ; 
envvars/COMPILE_WITHOUT_FOO: force 
    echo "$(COMPILE_WITHOUT_FOO)" > envvars/COMPILE_WITHOUT_FOO 
endif 

Jeśli chcesz wesprzeć mający niezdefiniowanych makr, trzeba będzie użyć ifdef lub ifndef warunkowe i mieć pewną wskazówkę w pliku, że wartość została niezdefiniowana ostatni raz było uruchomić.

Powiązane problemy