-
1. Data: 2012-05-06 13:09:10
Temat: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: "Andrzej Ekiert" <d...@t...pl>
Przedstawię najpierw scenariusz oraz problem:
Mamy bibliotekę modułów kodu źródłowego, które są używane w wielu
projektach. Każdy z modułów ma swoje parametry konfiguracyjne, którymi
można go "dostroić" dla potrzeb konkretnej aplikacji i płytki - np. moduł
będący driverem do scalaka radiowego ma m.in. parametry określające do
jakich pinów procesora scalak jest podpięty, moduł będący driverem do
pamięci na I2C ma parametry mówiące którym interfejsem I2C mikrokontrolera
należy z tą pamięcią rozmawiać, jaki jest rozmiar pamięci i który pin
procesora to CS, itp.
Problem:
W każdym projekcie, który korzysta z danego modułu, ustawiamy odpowiednio
wszystkie parametry. Następnie, z dowolnej przyczyny, w bibliotece
zmieniamy zestaw parametrów konfiguracyjnych oferowanych przez moduł. W
efekcie musimy poprawić pliki konfiguracyjne w każdym z projektów, co przy
rosnącej liczbie tych projektów po chwili staje się boleśnie pracochłonne.
Szukam mądrej metody radzenia sobie z tą niepotrzebną pracą. Metody
aplikowalnej do kodu w C i assemblerze (choć bardzo chętnie usłyszę, jeśli
w C++ da się to zrobić jakoś lepiej). Ważne, aby metoda nie powodowała
skutków run-time (np. zamiast ustalać rozmiar pamięci w konfiguracji, mogę
mieć zmienną ustawianą w funkcji inicjalizującej, do pinów mogę mieć
callbacki włącz/wyłącz, też ustawiane przez API, a do sprzętowego modułu
I2C mogę mieć dodatkową warstwę abstrakcji, ale wszystko to kosztuje
pamięć lub cykle, a w przypadku niektórych parametrów jest praktycznie
niewykonalne - poza tym tylko to przesuwa problem ze zmian konfiguracji,
na zmianę API).
Jestem blisko rozpoczęcia pisania programu, który będzie mi zarządzał tą
konfiguracją, tzn. za jednym klikiem porówna konfiguracje wszystkich
zarządzanych projektów, pokaże gdzie są nieustawione parametry, gdzie są
ustawione już nieistniejące parametry, gdzie wartość parametrów różni się
od domyślnej, a następnie umożliwi wybranie akcji, ustawienie właściwych
wartości i uaktualni zarządzane pliki konfiguracyjne. Ale trochę mam
nadzieję, że istnieje jakiś inteligentny sposób, na który nie wpadłem.
ae
-
2. Data: 2012-05-06 14:19:08
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: Zbych <z...@o...pl>
W dniu 06.05.2012 13:09, Andrzej Ekiert pisze:
> Problem:
> W każdym projekcie, który korzysta z danego modułu, ustawiamy
> odpowiednio wszystkie parametry. Następnie, z dowolnej przyczyny, w
> bibliotece zmieniamy zestaw parametrów konfiguracyjnych oferowanych
> przez moduł. W efekcie musimy poprawić pliki konfiguracyjne w każdym z
> projektów, co przy rosnącej liczbie tych projektów po chwili staje się
> boleśnie pracochłonne.
Na to nic nie poradzisz. Skoro biblioteka zmienia swoje
parametry/interface to musisz dostosować do niej programy.
W c++ od biedy można zastosować parametry domyślne albo przeciążyć
funkcje. Wtedy stare programy mogą korzystać ze starego interfejsu.
> Szukam mądrej metody radzenia sobie z tą niepotrzebną pracą. Metody
> aplikowalnej do kodu w C i assemblerze (choć bardzo chętnie usłyszę,
> jeśli w C++ da się to zrobić jakoś lepiej). Ważne, aby metoda nie
> powodowała skutków run-time (np. zamiast ustalać rozmiar pamięci w
> konfiguracji, mogę mieć zmienną ustawianą w funkcji inicjalizującej, do
> pinów mogę mieć callbacki włącz/wyłącz,
A to już zwykłe makra, definy, funkcje inline, specjalizacje szablonów
nie wystarczą do ukrycia fizycznego położenia pinów?
> sprzętowego modułu I2C mogę mieć dodatkową warstwę abstrakcji, ale
> wszystko to kosztuje pamięć lub cykle, a w przypadku niektórych
> parametrów jest praktycznie niewykonalne - poza tym tylko to przesuwa
> problem ze zmian konfiguracji, na zmianę API).
Jeśli do wszystkich modułów I2C z różnych procesorów jesteś w stanie
opracować jeden interface to nie ma problemu. Dodajesz do projektu plik
ze swoją obsługą I2C od danego procka i już. Moduł radiowy wykorzysta te
funkcje, które dołączy linker. Zero narzutu.
Ja raczej unikami używania tej samej kopii biblioteki do różnych
projektów. Dasz sobie głowę uciąć, że zmiana w bibliotece pod bieżący
projekt x nie spowoduje jakiś anomalii w projekcie x-10, który pisała
inna osoba?
Wolę zrobić kopię biblioteki z projektu x-1 i nanieść poprawki.
-
3. Data: 2012-05-06 14:21:22
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: Jacek Domański <j...@N...pl>
Ja osobiście jestem na początku drogi - tj. jeszcze nie powstały te
dziesiatki modulow do dziesiatek aplikacji, ale zaczynam już dostrzegać
problem, stąd moje zainteresowanie tą problematyką - tak, aby zawczasu
coś ustalić i pisać dalej w/g ustalonej, optymalnej metody.
Jednym z pomysłów jest wykorzystanie programów do zarządzania wersjami
typu: SVN, GIT, Mercurial.
Innym pomysłem, który mnie zainteresował, jest wykorzystanie linków do
plików zamiast kopii plików (pod Linuxem) - w ten sposób mamy tylko
jeden plik, widziany przez dowolnie wiele projektow i zmiana w nim
przenosi się automatycznie na wszystkie projekty.
Najprosciej byłoby nie zmieniać parametrow konfiguracyjnych w bibliotece
;-)
A może zostawić stare jako 'deprecated', a dodac do nich nowe?
--
Jado
W dniu 06.05.2012 13:09, Andrzej Ekiert pisze:
> Przedstawię najpierw scenariusz oraz problem:
>
> Mamy bibliotekę modułów kodu źródłowego, które są używane w wielu
> projektach. Każdy z modułów ma swoje parametry konfiguracyjne, którymi
> można go "dostroić" dla potrzeb konkretnej aplikacji i płytki - np.
> moduł będący driverem do scalaka radiowego ma m.in. parametry
> określające do jakich pinów procesora scalak jest podpięty, moduł będący
> driverem do pamięci na I2C ma parametry mówiące którym interfejsem I2C
> mikrokontrolera należy z tą pamięcią rozmawiać, jaki jest rozmiar
> pamięci i który pin procesora to CS, itp.
>
> Problem:
> W każdym projekcie, który korzysta z danego modułu, ustawiamy
> odpowiednio wszystkie parametry. Następnie, z dowolnej przyczyny, w
> bibliotece zmieniamy zestaw parametrów konfiguracyjnych oferowanych
> przez moduł. W efekcie musimy poprawić pliki konfiguracyjne w każdym z
> projektów, co przy rosnącej liczbie tych projektów po chwili staje się
> boleśnie pracochłonne.
>
> Szukam mądrej metody radzenia sobie z tą niepotrzebną pracą. Metody
> aplikowalnej do kodu w C i assemblerze (choć bardzo chętnie usłyszę,
> jeśli w C++ da się to zrobić jakoś lepiej). Ważne, aby metoda nie
> powodowała skutków run-time (np. zamiast ustalać rozmiar pamięci w
> konfiguracji, mogę mieć zmienną ustawianą w funkcji inicjalizującej, do
> pinów mogę mieć callbacki włącz/wyłącz, też ustawiane przez API, a do
> sprzętowego modułu I2C mogę mieć dodatkową warstwę abstrakcji, ale
> wszystko to kosztuje pamięć lub cykle, a w przypadku niektórych
> parametrów jest praktycznie niewykonalne - poza tym tylko to przesuwa
> problem ze zmian konfiguracji, na zmianę API).
>
> Jestem blisko rozpoczęcia pisania programu, który będzie mi zarządzał tą
> konfiguracją, tzn. za jednym klikiem porówna konfiguracje wszystkich
> zarządzanych projektów, pokaże gdzie są nieustawione parametry, gdzie są
> ustawione już nieistniejące parametry, gdzie wartość parametrów różni
> się od domyślnej, a następnie umożliwi wybranie akcji, ustawienie
> właściwych wartości i uaktualni zarządzane pliki konfiguracyjne. Ale
> trochę mam nadzieję, że istnieje jakiś inteligentny sposób, na który nie
> wpadłem.
>
> ae
-
4. Data: 2012-05-06 14:44:09
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: "Andrzej Ekiert" <d...@t...pl>
Dnia 06-05-2012 o 14:21:22 Jacek Domański <j...@n...pl>
napisał(a):
> Jednym z pomysłów jest wykorzystanie programów do zarządzania wersjami
> typu: SVN, GIT, Mercurial.
Ależ ja to wszystko mam pod Mercurialem, tyle że to tego problemu akurat
nie rozwiązuje.
> Innym pomysłem, który mnie zainteresował, jest wykorzystanie linków do
> plików zamiast kopii plików (pod Linuxem) - w ten sposób mamy tylko
> jeden plik, widziany przez dowolnie wiele projektow i zmiana w nim
> przenosi się automatycznie na wszystkie projekty.
Chodzi o to, że dla każdego projektu mam te same parametry ustawione w
inny sposób. Więc też nie rozwiązuje problemu.
> Najprosciej byłoby nie zmieniać parametrow konfiguracyjnych w bibliotece
> ;-)
No rzeczywiście :-)
ae
-
5. Data: 2012-05-06 14:55:59
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: "Andrzej Ekiert" <d...@t...pl>
Dnia 06-05-2012 o 14:19:08 Zbych <z...@o...pl> napisał(a):
> A to już zwykłe makra, definy, funkcje inline, specjalizacje szablonów
> nie wystarczą do ukrycia fizycznego położenia pinów?
Wystarczą, ale dla każdego projektu trzeba te define'y inaczej ustawić -
ta sama nazwa, inna wartość.
> Jeśli do wszystkich modułów I2C z różnych procesorów jesteś w stanie
> opracować jeden interface to nie ma problemu. Dodajesz do projektu plik
> ze swoją obsługą I2C od danego procka i już. Moduł radiowy wykorzysta te
> funkcje, które dołączy linker. Zero narzutu.
Przy wielu architekturach, to akurat nie mam wyjścia i muszę zrobić
takiego HALa, ale narzut jest. W przypadku jednej architektury, to zamiast
po prostu się odwołać do rejestru sprzętowego modułu, muszę przekazać
mojemu driverowi do chipu jakąś strukturę drivera do modułu I2C, która
będzie mieć np. callbacki do funkcji pośredniczących. Narzut jak diabli,
choć czasem trzeba się na niego zgodzić (np. wspódzielony dostęp kilku
"driverów" do jednego sprzętowego I2C).
> Ja raczej unikami używania tej samej kopii biblioteki do różnych
> projektów. Dasz sobie głowę uciąć, że zmiana w bibliotece pod bieżący
> projekt x nie spowoduje jakiś anomalii w projekcie x-10, który pisała
> inna osoba?
Staram się tak modularyzować kod i dawać takie API, żeby zmiany nie
wywoływały efektów ubocznych. Oczywiście przetestować to zawsze trzeba i
nie dam sobie uciąć nawet paznokcia.
> Wolę zrobić kopię biblioteki z projektu x-1 i nanieść poprawki.
Wykrywasz błąd albo robisz usprawnienie w x-1 i dopiero masz poprawianie
wszędzie gdzie ta kopia jest. Brrr...
ae
-
6. Data: 2012-05-06 15:15:04
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: Sebastian Biały <h...@p...onet.pl>
On 2012-05-06 13:09, Andrzej Ekiert wrote:
> że istnieje jakiś inteligentny sposób, na który nie
> wpadłem.
Zapropnuje sposób trywialny, być może akurat będzie w sam raz:
*** i2cdriver.c ***
#include "i2cconfiguration.h"
#include "../lib/i2c/i2ccore.c"
#include "../lib/i2c/i2chardware.c"
*** i2cdriver.h ***
#include "i2cconfiguration.h"
#include "../lib/i2c/i2cinterface.h"
Plik i2cconfiguration.h jest częscią konkretnego projektu, podobnie jak
i2cdriver.c.
I tyle. W i2cconfiguratiion stado #define per projekt. W plikach lib/i2c
(h i c) od groma #ifdef na każdy wariant.
Powinno być najmniej intruzywne i najłatwiejsze w utrzymaniu przez
programistę C. Żadnych zmian w bibliotece. Wada: kompilujesz wszystko za
pierwszym razem. ALE. Dzieki make potem kompilujesz tylko zmiany, więc
narzut jest jednorazowy.
-
7. Data: 2012-05-06 15:23:06
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: Zbych <z...@o...pl>
W dniu 06.05.2012 14:55, Andrzej Ekiert pisze:
> Dnia 06-05-2012 o 14:19:08 Zbych <z...@o...pl> napisał(a):
>
>> A to już zwykłe makra, definy, funkcje inline, specjalizacje szablonów
>> nie wystarczą do ukrycia fizycznego położenia pinów?
>
> Wystarczą, ale dla każdego projektu trzeba te define'y inaczej ustawić -
> ta sama nazwa, inna wartość.
Zgadza się i zakładam, że rzeczy specyficzne dla projektu trzymasz w
osobnym pliku, który leży sobie w katalogu z projektem i jest przez
bibliotekę tylko includowany. Zgadza się?
>> Jeśli do wszystkich modułów I2C z różnych procesorów jesteś w stanie
>> opracować jeden interface to nie ma problemu. Dodajesz do projektu
>> plik ze swoją obsługą I2C od danego procka i już. Moduł radiowy
>> wykorzysta te funkcje, które dołączy linker. Zero narzutu.
>
> Przy wielu architekturach, to akurat nie mam wyjścia i muszę zrobić
> takiego HALa, ale narzut jest. W przypadku jednej architektury, to
> zamiast po prostu się odwołać do rejestru sprzętowego modułu, muszę
> przekazać mojemu driverowi do chipu jakąś strukturę drivera do modułu
> I2C, która będzie mieć np. callbacki do funkcji pośredniczących. Narzut
> jak diabli, choć czasem trzeba się na niego zgodzić (np. wspódzielony
> dostęp kilku "driverów" do jednego sprzętowego I2C).
Ja jak na razie na I2c wieszałem jakieś pamięci, RTC itp. badziewie. Do
jego obsługi wystarczały mi 3 funkcje typu wyślij blok danych, odbierz
blok danych, sprawdź gotowość. Współdzielenie zabezpieczałem mutexami.
Funkcje RTC czy obsługa pamięci wprost wołały te funkcje. W innym procku
dodawałem tylko inną bibliotekę do I2c. Interface zostawał ten sam. Zero
narzutu.
>> Ja raczej unikami używania tej samej kopii biblioteki do różnych
>> projektów. Dasz sobie głowę uciąć, że zmiana w bibliotece pod bieżący
>> projekt x nie spowoduje jakiś anomalii w projekcie x-10, który pisała
>> inna osoba?
>
> Staram się tak modularyzować kod i dawać takie API, żeby zmiany nie
> wywoływały efektów ubocznych. Oczywiście przetestować to zawsze trzeba i
> nie dam sobie uciąć nawet paznokcia.
>
>> Wolę zrobić kopię biblioteki z projektu x-1 i nanieść poprawki.
>
> Wykrywasz błąd albo robisz usprawnienie w x-1 i dopiero masz poprawianie
> wszędzie gdzie ta kopia jest. Brrr...
To jest niewątpliwie wada. Ale powiedzmy sobie szczerze, ile można
spieprzyć w kodzie obsługi I2C, uarta itp?
A teraz weź projekt sprzed kilku lat (bo klient chce drobną poprawkę) i
skompiluj go z nową biblioteką. Jaką masz gwarancję, że nie wyjdą jakieś
wredne bugi związane np. z zależnościami czasowymi?
-
8. Data: 2012-05-06 15:30:28
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: "Andrzej Ekiert" <d...@t...pl>
Dnia 06-05-2012 o 15:15:04 Sebastian Biały <h...@p...onet.pl>
napisał(a):
> Zapropnuje sposób trywialny, być może akurat będzie w sam raz:
>
> *** i2cdriver.c ***
>
> #include "i2cconfiguration.h"
> #include "../lib/i2c/i2ccore.c"
> #include "../lib/i2c/i2chardware.c"
>
> *** i2cdriver.h ***
>
> #include "i2cconfiguration.h"
> #include "../lib/i2c/i2cinterface.h"
>
> Plik i2cconfiguration.h jest częscią konkretnego projektu, podobnie jak
> i2cdriver.c.
>
> I tyle. W i2cconfiguratiion stado #define per projekt. W plikach lib/i2c
> (h i c) od groma #ifdef na każdy wariant.
>
Ale to mi w żaden sposób nie dotyka mojego problemu. Jeśli odwołam się w
"../lib/i2c/i2ccore.c" do nowego parametru konfiguracyjnego C_I2C_SHMOO,
to muszę go ręcznie zdefiniować w każdym i2cconfiguration.h w każdym
projekcie. Jeśli zmienię nazwę i trochę funkcje parametru C_I2C_FOO na
C_I2C_BAR, to znowu zmiana w każdym projekcie. Chodzi mi o sposób lub
narzędzie do automatyzacji takich zmian: wykrywanie niedodanych definicji,
eliminację przestarzałych definicji, itp.
Samo definiowanie konfiguracji per-projekt i jej #include w plikach
biblioteki to mam rozwiązane w miarę elegancko. Boli mnie tylko potrzeba
ręcznego czyszczenia konfiguracji per-projekt w wypadku zmian w opcjach
oferowanych przez bibliotekę.
ae
-
9. Data: 2012-05-06 15:44:02
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: "Andrzej Ekiert" <d...@t...pl>
Dnia 06-05-2012 o 15:23:06 Zbych <z...@o...pl> napisał(a):
>>> A to już zwykłe makra, definy, funkcje inline, specjalizacje szablonów
>>> nie wystarczą do ukrycia fizycznego położenia pinów?
>>
>> Wystarczą, ale dla każdego projektu trzeba te define'y inaczej ustawić -
>> ta sama nazwa, inna wartość.
>
> Zgadza się i zakładam, że rzeczy specyficzne dla projektu trzymasz w
> osobnym pliku, który leży sobie w katalogu z projektem i jest przez
> bibliotekę tylko includowany. Zgadza się?
>
Tak.
> Ja jak na razie na I2c wieszałem jakieś pamięci, RTC itp. badziewie. Do
> jego obsługi wystarczały mi 3 funkcje typu wyślij blok danych, odbierz
> blok danych, sprawdź gotowość. Współdzielenie zabezpieczałem mutexami.
> Funkcje RTC czy obsługa pamięci wprost wołały te funkcje. W innym procku
> dodawałem tylko inną bibliotekę do I2c. Interface zostawał ten sam. Zero
> narzutu.
Masz driver do RTC w bibliotece. Procesor ma 2 moduły sprzętowe I2C1 i
I2C2. Musisz przekazać driverowi RTC informację, funkcje dotyczące którego
modułu ma wywołać: odbierz_blok_danych_z_I2C1() czy
odbierz_blok_danych_z_I2C2(). Albo to robisz na poziomie #define, albo
definiując "driver" do I2C i przekazując modułowi I2C handle do tego
drivera (jest narzut). Ja chcę na poziomie #define, ale pragnę sobie
usprawnić zarządzanie takimi #define.
>>> Wolę zrobić kopię biblioteki z projektu x-1 i nanieść poprawki.
>>
>> Wykrywasz błąd albo robisz usprawnienie w x-1 i dopiero masz poprawianie
>> wszędzie gdzie ta kopia jest. Brrr...
>
> To jest niewątpliwie wada. Ale powiedzmy sobie szczerze, ile można
> spieprzyć w kodzie obsługi I2C, uarta itp?
Spieprzyć zawsze można. Poza tym moduł może być czymś bardziej złożonym.
Np. implementacją protokołu sieciowego.
> A teraz weź projekt sprzed kilku lat (bo klient chce drobną poprawkę) i
> skompiluj go z nową biblioteką. Jaką masz gwarancję, że nie wyjdą jakieś
> wredne bugi związane np. z zależnościami czasowymi?
Gwarancji nie mam, choć moja ciągła dbałość o to, by kawałek kodu, w
którym zależności czasowe występują, nie mógł być zakłócony przez inne
niezwiązane z nim moduły wykonywane równolegle, daje mi spore szanse.
Generalnie uważam, że zysk ze współdzielonych bibliotek znacznie
przewyższa koszty.
ae
-
10. Data: 2012-05-06 15:49:54
Temat: Re: [OT] Zarządzanie konfiguracją modułów kodu źródłowego
Od: Sebastian Biały <h...@p...onet.pl>
On 2012-05-06 15:30, Andrzej Ekiert wrote:
> Ale to mi w żaden sposób nie dotyka mojego problemu. Jeśli odwołam się w
> "../lib/i2c/i2ccore.c" do nowego parametru konfiguracyjnego C_I2C_SHMOO,
> to muszę go ręcznie zdefiniować w każdym i2cconfiguration.h w każdym
> projekcie.
#include "../lib/i2c/defaultconfiguration.h"
#include "i2cconfiguration.h"
To powinno zadzialać jak gdyby dziedziczenie parametrów. Możesz też uzyć
#ifndef FOO, #define FOO DEFAULR_FOO.
Ewentualnie, znacznie bezpieczniej, #ifndef FOO, #error "FOO not set"
> Jeśli zmienię nazwę i trochę funkcje parametru C_I2C_FOO na
> C_I2C_BAR, to znowu zmiana w każdym projekcie. Chodzi mi o sposób lub
> narzędzie do automatyzacji takich zmian: wykrywanie niedodanych
> definicji, eliminację przestarzałych definicji, itp.
Najlepiej, gdybys tego nie robił w ogóle. takie narzędzie jest
niebezpieczne. Wydaje mi się, że najbezpieczniej jest zdać się na
kompilator. Czyli raz na jakiś czas budujesz wszystkie swoje żywe
projekty w całości i poprawiasz tam gdzie padła kompilacja.
Podobnie do tego pomysłu działa konfigurator opcji kompilacji linuxa
(kernela). Możesz sobie wyobrazić że tak jest ich dużo i że pojawia się
niedostrzegana wcześniej warstwa - zależności. W dodatku są ustalane
ręcznie. Np. sterownik do foobar można kompilowac tylko wtedy gdy jest
scsi itp. Takie zalezności są cieżkie w utrzymaniu bo nie wynikają
wprost z kodu tylko z jakieś metawiedzy poza.
> Samo definiowanie konfiguracji per-projekt i jej #include w plikach
> biblioteki to mam rozwiązane w miarę elegancko. Boli mnie tylko potrzeba
> ręcznego czyszczenia konfiguracji per-projekt w wypadku zmian w opcjach
> oferowanych przez bibliotekę.
Tego nie unikniesz w przypadku ogólnym. Jesli i2cdriver_init przyjmie 2
parametry a nie jeden to i tak musisz zmienić *wszystkie* projekty.
Wielu programistów C wpada tutaj na genialny pomysł uzycia makr albo
domyslnych parametrów, ale do ślepa uliczka. Tak czy inaczej refaktoring
bibliteki generuje zmiany po stronie klientów.
Jesli masz klienta martwego, ale mimo to chcesz utrzymać kompilację, to
czasami wystarczy kod forkować, czyli #include
"../lib/i2c/v2/i2ccode.c". Nie jest to eleganckie, ale w przypadku
embedded pozwala projekt zamrozić. Problemem jest backportowanie poprawek.
Ten sposob jest uzywany też na linuxie, wystarczy zobaczyć katalog /lib
żeby zauważyć wiele róznych wersji bibliotek. Głównie dla supportu
starych klientów.