2011-09-30 13 views
6

Szukam wysokiego poziomu systemu/narzędzia, które pomoże uporządkować mój osadzony projekt C w "modułach" i "komponentach". Zwróć uwagę, że te dwa pojęcia są bardzo subiektywne, więc moje definicje podano poniżej.System kompilacji dla osadzonego projektu C/C++

  • Moduł jest spójną kolekcją plików c i h, ale zawiera tylko jeden publiczny plik h, który jest widoczny dla innych modułów.
  • Z drugiej strony komponent (lub warstwa) to zbiór modułów (np. Warstwa aplikacji, warstwa biblioteki, warstwa sterownika, warstwa RTOS itd.).

System build/narzędzie powinno -

  • Zapobieganie cykliczne zależności między komponentami i modułami (zależności cyklicznych wewnątrz modułów jest w porządku)
  • uniemożliwić dostęp do prywatnych barier moduł. Jeśli inne moduły próbują dołączyć plik nagłówka, który jest prywatny dla modułu, system kompilacji musi wygenerować błąd. Pliki znajdujące się w prywatnej barierze muszą jednak zawierać inne pliki wewnątrz tej bariery.
  • budowlanych wsparcia i automatycznie wykonywanie testów jednostkowych (szybka pętla sprzężenia zwrotnego dla TDD) na gospodarza
  • testy zespołu wsporczego do uruchomienia na symulatorze docelowej
  • posiada kod analizy statycznej
  • generowania kodu posiada kod
  • wsparcie wykrywania powielania (egzekwować zasadę DRY)
  • kod
  • wsparcie upiększanie
  • generacja wspierająca jednostki kod testowy metryki pokrycia
  • generacja wsparcie metrykami jakości kodu
  • być niezależny od platformy

Mógłbym napisać własny narzędzie budowania i spędzają dużo czasu na to. Nie jest to jednak moja dziedzina wiedzy i wolałbym nie wymyślać ponownie koła, jeśli ktoś już stworzył takie narzędzie.

Odpowiedz

4

Konwencjonalnym sposobem osiągnięcia tego byłoby umieszczenie kodu źródłowego każdego modułu w oddzielnym katalogu. Każdy katalog może zawierać wszystkie pliki źródłowe i pliki nagłówkowe dla modułu.

Publiczny nagłówek każdego modułu można umieścić w osobnym, wspólnym katalogu nagłówków. Prawdopodobnie użyłbym dowiązania symbolicznego ze wspólnego katalogu do odpowiedniego katalogu modułów dla każdego nagłówka.

Reguły kompilacji po prostu stwierdzają, że żaden moduł nie może zawierać nagłówków z innych modułów, z wyjątkiem nagłówków we wspólnym katalogu. Daje to wynik, że żaden moduł nie może zawierać nagłówków z innego modułu - z wyjątkiem publicznego nagłówka (w ten sposób egzekwując prywatne bariery).

Zapobieganie cyklicznym zależnościom nie jest automatyczne. Problem polega na tym, że można ustalić, że istnieje cykliczna zależność, patrząc na kilka plików źródłowych naraz, a kompilator analizuje tylko jeden plik naraz.

Rozważ parę modułów, moduł A i moduł B, a także program, Program1, który korzysta z obu modułów.

base/include 
     ModuleA.h 
     ModuleB.h 
base/ModuleA 
     ModuleA.h 
     ModuleA1.c 
     ModuleA2.c 
base/ModuleB 
     ModuleB.h 
     ModuleB1.c 
     ModuleB2.c 
base/Program1 
     Program1.c 

Kompilując Program1.c, jest to w pełni uzasadnione gdyż obejmuje zarówno ModuleA.h i ModuleB.h jeśli to sprawia, że ​​korzystanie z usług obu modułów. Tak więc ModuleA.h nie może narzekać, jeśli ModuleB.h znajduje się w tej samej jednostce tłumaczeniowej (TU), a moduł ModuleB.h nie może również złożyć zażalenia, jeśli ModuleA.h jest zawarty w tej samej JT.

Załóżmy, że moduł A jest uzasadniony w korzystaniu z urządzeń z Modułu B. Dlatego podczas kompilowania ModuleA1.c lub ModuleA2.c nie ma problemu z włączeniem zarówno ModuleA.h, jak i ModuleB.h.

Jednak, aby zapobiec cyklicznym zależnościom, użytkownik musi mieć możliwość zablokowania kodu w ModuleB1.c i ModuleB2.c przed użyciem ModuleA.h.

O ile widzę, jedynym sposobem na zrobienie tego jest jakaś technika, która wymaga prywatnego nagłówka dla modułu B, który mówi "ModułA jest już zawarty", mimo że nie jest, i to jest zawarte przed ModuleA.h jest zawsze włączony.

Szkielet ModuleA.h będzie standardowy format (i ModuleB.h będzie podobna):

#ifndef MODULEA_H_INCLUDED 
#define MODULEA_H_INCLUDED 
...contents of ModuleA.h... 
#endif 

Teraz, jeśli kod w ModuleB1.c zawiera:

#define MODULEA_H_INCLUDED 
#include "ModuleB.h" 
...if ModuleA.h is also included, it will declare nothing... 
...so anything that depends on its contents will fail to compile... 

To nie jest automatyczne.

Można wykonać analizę załączonych plików i wymagać, aby istniał topologiczny rodzaj zależności bez pętli. Wcześniej był program tsort w systemach UNIX (i programie towarzyszącym, lorder), który razem zapewniał potrzebne usługi, aby utworzyć bibliotekę statyczną (.a) zawierającą pliki obiektów w kolejności, która nie wymagała ponownego skanowania archiwum. Program ranlib, a ostatecznie ar i ld przejął obowiązki zarządzania ponownym skanowaniem pojedynczej biblioteki, w ten sposób powodując w szczególności nadmiarowość. Ale tsort ma bardziej ogólne zastosowania; jest dostępny na niektórych systemach (na przykład MacOS X, RHEL 5 Linux).

Tak więc, korzystając ze śledzenia zależności z GCC plus tsort, powinieneś być w stanie sprawdzić, czy istnieją cykle między modułami. Ale trzeba to potraktować ostrożnie.

Może być jakiś IDE lub inny zestaw narzędzi, który automatycznie obsłuży te rzeczy. Ale zwykle programiści mogą być wystarczająco zdyscyplinowani, aby uniknąć problemów - o ile wymagania i zależności między modułami są dokładnie udokumentowane.

+0

+1 Świetna odpowiedź, w szczególności ostatni akapit. – mouviciel

+0

Jonathan, jakie byłoby narzędzie wyboru do wdrożenia proponowanego rozwiązania? Make, CMake, Rake lub coś zupełnie innego? – thegreendroid

+1

Nie mam zdecydowanych poglądów na bardziej nowoczesne systemy; Widziałem, jak wychwalał CMake, ale prawdopodobnie mógłbyś użyć dowolnego z nich. Może to zależeć częściowo od tego, które języki są ci najbardziej znane. Jeśli używasz Ruby, wtedy Rake ma sens, na przykład. Nadal używam zwykłego starego Make, ale robię to przez ... raczej długi czas ... więc czuję się w tym komfortowo. Jest to rozwiązanie o najniższym wspólnym mianowniku, z problemami z przenośnością. Czasami używam Autoconf, aby poradzić sobie z problemami przenośności, ale ma on swoją własną złożoną złożoność (i dramatycznie zwiększa rozmiary małych projektów). –

2

Dla ogólnego rozwiązania, w pełni polecam pójść z rozwiązaniem Jonathana Lefflera. Jednakże, jeśli absolutnie potrzebujesz automatycznego testu, czy twoje moduły są samodzielne i izolowane, możesz spróbować systemu kompilacji Debiana.

Zapakuj każdy moduł do pakietu Debiana (który jest wykonywany bardzo szybko, gdy jest już autoconf'd), deklaruj Build-Depends i buduj pakiety wewnątrz środowiska pbuilder. To zapewnia, że ​​tylko publiczne nagłówki każdego modułu są dostępne (ponieważ tylko te są w pakietach .deb, które są instalowane przez pbuilder do budowania innych pakietów) i są doskonałe narzędzia do przeglądania drzew pakietów Debiana i upewniania się, że są one cykliczne. wolny.

Jest to prawdopodobnie nadmierne zabicie. Po prostu stwierdzam, że jest kompletny, a przypadek zdecydowanie wymaga automatycznego rozwiązania.

Powiązane problemy