Sytuacja kobiet w IT w 2024 roku
14.10.20194 min
Sander Mertens

Sander MertensSenior Software EngineerZoox

Rozwiązanie problemu zależności w C/C++ dzięki Bake Bundles

Poznaj funkcję Bake Bundles i sprawdź, jak dzięki niej ograniczyć problemy związane z zależnościami w C i C++.

Rozwiązanie problemu zależności w C/C++ dzięki Bake Bundles

Praca z projektami, które wymagają wielu repozytoriów Git, może być żmudna. Przy każdej istotnej zmianie API istnieje szansa, że wiele repozytoriów będzie wymagało aktualizacji. Jeśli zdarza się to dość regularnie, wzrasta prawdopodobieństwo wystąpienia błędów. Nawet, jeśli jesteś jedynym kontrybutorem wszystkich tych repozytoriów, to utrzymywanie ich w stanie, w którym ktoś inny może je sklonować i zbudować, jest uciążliwe.

Wielokrotnie miałem ten problem z Flecs - systemem komponentów dla C99, który składa się z głównego repozytorium i kilku „modułów”, które znajdują się w oddzielnych repozytoriach. Ilekroć publikowałem przykład Flecsa na Reddicie, byłem prawie pewien, że otrzymam feedback kilka tygodni lub miesięcy później, że  kompilacja się komuś nie powiodła.

Postanowiłem coś z tym w końcu zrobić i zaprojektować nową funkcję bake o nazwie „bundles”, która będzie kolekcją repozytoriów i wersji. Chciałem przeskoczyć pewne ograniczenia dotyczące projektu:

  • Nie powinno być potrzeby wspierania infrastruktury innej niż git
  • Użytkownicy powinni mieć możliwość definiowania własnych zależności i wersji
  • Użytkownicy powinni mieć możliwość zainstalowania/uruchomienia projektu za pomocą jednego polecenia, bezpośrednio z repozytorium git
  • Zainstalowane wersje powinny być deterministyczne, a wizualizacja tego, co zostanie zainstalowane, powinna być łatwa
  • Nie powinno istnieć (potencjalne) udostępnianie danych stronom trzecim
  • Konfiguracja powinna być DRY i łatwa do udostępniania między projektami


Czym są bundles? Ich idea polega na tym, że programiści tworzą plik JSON z zestawem repozytoriów i wersji, o których wiadomo, że dobrze ze sobą współpracują. Może to następnie zapewnić stabilną bazę do budowy aplikacji w oparciu o zestaw repozytoriów. Najpierw spójrzmy na podstawowy plik projektu:

{
  "id": "apple_pie",
  "value": {
    "use": ["cinnamon"]
  }
}


Wystąpił problem z tym plikiem. Bake nie ma pojęcia, gdzie można znaleźć example_library, więc dopóki użytkownik nie sklonuje tego repozytorium, bake nie będzie w stanie zbudować example. Dzięki pakietom, możemy teraz rozwiązać ten problem, dodając sekcję do naszego projektu:

{
  "id": "apple_pie",
  "value": {
    "use": ["cinnamon"]
  },
  "bundle": {
    "repositories": {
      "cinnamon": "https://github.com/SanderMertens/cinnamon"
    }
  }
}


Teraz możemy po prostu uruchomić nasz projekt, a bake automatycznie sklonuje repozytorium cinnamon, jeśli nie zostało jeszcze zainstalowane. Jednak nadal nie określamy, której wersji cinnamon chcemy użyć. Możemy to zrobić, dodając sekcję refs do konfiguracji:

{
  "id": "apple_pie",
  "value": {
   "use": ["cinnamon"]
  },
  "bundle": {
    "repositories": {
      "cinnamon": "https://github.com/SanderMertens/cinnamon"
    },
    "refs": {
      "default": {
        "cinnamon": {
          "tag": "v1.0"
        }
      }
    }
  }
}


To polecenie instruuje bake, aby przełączył się na tag v1.0, gdy sklonuje repozytorium dla cinnamon. Możemy również określić dowolną gałąź, a jeśli chcemy określić SHA commita zamiast tagu, możemy to również zrobić w tym miejscu.

Takie konfiguracje mogą być niewygodne, szczególnie jeśli istnieje wiele repozytoriów i wersji. Aby upewnić się, że nie będą się powtarzać w każdym projekcie, bake umożliwia zdefiniowanie pakietów w ich własnym projekcie, jak poniżej:

{
  "id": "baking_bundle",
  "type": "package",
  "value": {
    "language": "none"
  },
  "bundle": {
    "default-host": "https://github.com"
    "repositories": {
      "apple": "SanderMertens/apple",
      "sugar": "SanderMertens/sugar",
      "cinnamon": "SanderMertens/cinnamon"
    },
    "refs": {
      "v1.0": {
        "default-tag": "v1.0"
        "sugar": {
          "commit": "d147bb3"
        }
      },
      "v2.0": {
        "default-tag": "v2.0"
      }
    }
  }
}


Zauważ, że tam, gdzie w poprzednim projekcie mieliśmy pakiet default, mamy teraz dwa pakiety, v1.0 i v2.0. Dla każdego z nich wszystkie repozytoria zostaną ustawione odpowiednio za pomocą tagów v1.0 i v2.0, z wyjątkiem projektu sugar, który w pakiecie v1.0 wymaga ustawienia na określony SHA.

W projekcie apple_pie możemy teraz zmienić nasz plik projektu na:

{
  "id": "apple_pie",
  "value": {
    "use": ["cinnamon", "sugar", "apple"],
    "use-bundle": ["baking_bundle:v1.0"]
  },
  "bundle": {
    "repositories": {
      "baking_bundle": "https://github.com/baking/baking_bundle"
    }
  }
}


Dodałem konfigurację pakietu, aby bake mógł znaleźć projekt baking_bundle, na wypadek, gdyby nie był dostępny lokalnie. W ten sposób zapewniamy, że nasz projekt zawsze będzie klonowany z odpowiednimi repozytoriami i wersjami dla naszych zależności, bez względu na stan naszego środowiska.

Na koniec możesz rozkazać bake'owi, aby dodał pakiet do swojego środowiska. Zapewni to, że środowisko pozostanie w stanie określonym w pakiecie. Jeśli projekt spróbuje sklonować repozytorium, które jest w innej wersji niż ta określona w pakiecie, bake je odrzuci. Aby skonfigurować projekt do korzystania z określonego pakietu, po prostu wykonaj:

bake use baking_bundle:v1.0


Jeśli chcesz zobaczyć, co zawiera ten pakiet, możesz uruchomić:

bake list --show-repositories


Wynik będzie wyglądał mniej więcej tak:

W tym przykładzie załadowałem pakiet flecs.hub:default, który zawiera projekt Flecs i moduły. Pomarańczowe repozytoria znajdują się w pakiecie, ale nie są instalowane lokalnie. Aby zainstalować, mogę po prostu zrobić:

bake install flecs.systems.physics


Sam zacząłem już korzystać z pakietów i wpłynęło to znacznie na wygodę mojej pracy. Jestem pewien, że z biegiem czasu wymyślę więcej przypadków użycia, które głębiej zintegrują pakiety z funkcjami bake.

Jeśli chcesz dowiedzieć się więcej, sprawdź dokumentację w repozytorium bake.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>