wtorek, 15 lipca 2008
968... czyli jak zostałem MCP
Zaczęło się całkiem sympatycznie - rejestracja, później kawa w oczekiwaniu aż się egzamin "ściągnie" (cokolwiek by to miało znaczyć ;). Po wejściu do sali egzaminacyjnej, krótki instruktarz i zaczęło się. Wylosowałem 47 pytań, na których rozwiązanie miałem ponad cztery godziny(!). Po pierwszych pięciu stwierdziłem, że to będzie banalnie proste... po dwudziestu już nie byłem tego taki pewien, bo pojawiły się trudniejsze pytania. Po półtorej godzinie udzieliłem odpowiedzi na wszystkie pytania i wykonałem jedną symulacje jaka mi się trafiła.
Zostało duuuuużo czasu. Stwierdziłem, że zastanawiać się nad udzielonymi odpowiedziami, po pierwsze, nie chce mi się, a po drugie, mógłbym jeszcze coś spieprzyć zbyt dużo myśląc, więc zakończyłem egzamin. Chwila niepewności.... i jest wynik - tytułowe 968. Zajebiście. Tego się nie spodziewałem, miałem nadzieję na 900, ale realnie zakładałem 850.
Po wyniku możnaby przypuszczać że zawaliłem jedno, góra dwa, pytania. Być może były to te dwa pytania z WSUSa, na które kompletnie strzelałem.
Następny w kolejce jest 70-290, a na razie czekam na maila z Microsoftu z potwierdzeniem zdania egzaminu.
poniedziałek, 14 lipca 2008
Czwarta cywilizacja czwarta

Dochodzę do wniosku, że jestem chyba graczem-gawędziarzem. Więcej mówię o graniu w swoje ulubione pozycje niż to rzeczywiście robię.
Civilization IV, polska premiera - 25 listopada 2005, pudełko stało na półce 30 listopada, łącznie zagrałem w nią trzy razy (w sensie zagrałem i ukończyłem). Civilization IV: Warlords, polska premiera - 25 sierpnia 2006, pudełko stało na półce dwa dni później, zagrałem raptem jeden raz. Civilization IV: Beyond the Swords, premiera - 30 lipca 2007, pudełko przyszło 10 sierpnia. W tę ostatnią udało mi się zagrać kilka razy(!) w przeciągu bez mała roku.
Teraz czytam informacje o nowym dodatku i już się oczywiście napalam na kupno, sprawdzam czy przypadkiem mój sprzet nie będzie wymagał upgrade'u, notuję w PIMie żeby nie przegapić premiery. Ostatecznie, stanie się kolejną ozdobą na półce, w którą zagram może dwa razy. A może nie ;)
...i jeszcze raz
poniedziałek, 28 stycznia 2008
Eppur si muove...
Z ciekawostek, jako że nikt mi nigdy nie powiedział, że do budowania jądra nie da się zaprząc autoconfa i automake'a. I z drugiej strony, z braku pomysłu na szybkie w implementacji narzędzie do budowania tegoż, spróbowałem użyć do tego celu właśnie autotoolsy… I udało się. Pomijam, że regeneracja plików configure i wszystkich Makefile'ów trwa dłuzej niż samo budowanie kernela, kto by sie tym przejmował.
Teraz, kolejny duży krok to będzie instalator… ale kiedy?
wtorek, 24 kwietnia 2007
Winowajca odnaleziony
czwartek, 19 kwietnia 2007
New look and feel
środa, 7 marca 2007
PLD/FreeBSD Preview 1
Po uruchomieniu maszyny i zalogowaniu się na roota (hasło puste) trzeba skonfigurować sieć:
ifconfig lnc0 192.168.1.2albo wyedytować znane z PLD pliki odpowiedzialne za powyższe i zrestartować usługę
route add default 192.168.1.1
echo "nameserver 192.168.1.1" > /etc/resolv.conf
network /etc/rc.d/init.d/network restartI już można działać. Nie polecam instalowania pakietu
kernel i loader bo te które są na FTPie mogą kompletnie rozwalić system. Zainteresowanym życzę miłej zabawy.
Jak tylko znajdę chwilę czasu postaram się opisać jak zainstalować PLD/FreeBSD na fizycznej maszynie przy pomocy płyty instalacyjnej FreeBSD.
wtorek, 6 marca 2007
Do builderów podejscie trzecie
czwartek, 15 lutego 2007
Ciekawa własność NULLa
Najprościej bedzie na przykladzie. Załózmy, że mamy dwie tabele "process" i "task". Tabela "process" zawiera listę procesów wykonywanych przez pewien abstrakcyjny procesor, na każdy proces składa się pewna ilość zadań, przy czym każde zadanie może należeć tylko do jednego procesu; czyli prosta relacja jeden do wielu. Dodatkowo każde zadanie posiada właściwość "status" określająca stan tego zadania. Dla ustalenia uwagi niech pole "status" może przyjmować trzy wartości: "none", "running" lub "completed". Definicja tych tabel może wyglądac tak:
CREATE TABLE process (
id INT UNSIGNED PRIMARY KEY NOT NULL auto_increment,
name VARCHAR);
CREATE TABLE task (
id INT UNSIGNED PRIMARY KEY NOT NULL auto_increment,
id_process INT UNSIGNED NOT NULL,
status ENUM('none', 'running', 'completed') NOT NULL);
Zapytanie które zwróci nam identyfikator procesu wraz z ilością zadań przyporzadkowanych do tego procesu jest oczywiście trywialne i wyglada tak:
SELECT id_process, COUNT(*) FROM task GROUP BY id_process;
Problemem natomiast jest zapytanie zwracajace identyfikator procesu wraz z ilością zadań przyporzadkowanych do tego procesu, ale takich które nie zostały jeszcze zakończone, czyli mają status różny od 'completed'. Proste:
SELECT id_process, COUNT(status != 'completed') FROM task GROUP BY id_process;nie wystarcza ponieważ
COUNT zlicza zgrupowane wartości bez wzgledu na ich wartość. W tym przypadku zarówno TRUE jak i FALSE jest traktowane jako element to zliczenia. Zatem powyższe zapytanie zwraca to samo co poprzednie, czyli identyfikator procesu wraz z ilością zadań przyporzadkowanych do tego procesu. Możnaby oczywiście uzyć funkcji SUM i IF w następujący sposób:
SELECT id_process, SUM(IF(status != 'completed), 1, 0) FROM task GROUP BY id_process;Ale to nie dość, że niezgodne z zasadami sztuki, to jeszcze mało czytelne i pewnie mało wydajne.
Aby w prosty sposób uzyskać oczekiwany wynik wystarczy wykorzystać to że wartości NULL są niepoliczalne dla funkcji COUNT. Jeżeli zgrupujemy rekordy które w zadanym polu maja cztery wartości z czego dwie są równe NULL to funkcja COUNT na tym polu zwróci nam 2, a nie 4.
Wystarczy zatem doprowadzić ostatnie zapytanie wykorzystujące funkcję COUNT do takiej postaci, w której wartość argumentu jest policzalna gdy pole "status" jest inne niż 'completed', a niepoliczalna (czyli równa NULL) w przeciwnym wypadku. Wyrażenie
status != 'completed' OR NULLspełnia te założenia ponieważ przyjmuje jedną z dwóch wartości:
TRUE dla pola "status" różnego od 'completed', albo NULL w przeciwnym wypadku. Ostatecznie zapytanie zwracajace identyfikator procesu wraz z ilością zadań przyporzadkowanych do tego procesu, takich które nie zostały jeszcze zakończone wygląda tak:
SELECT id_process, COUNT(status != 'completed' OR NULL) FROM task GROUP BY id_process;
Śliczne, prawda?
wtorek, 6 lutego 2007
Dlaczego CVS ssie...
Struktura repozytorium różni sie znacząco od płaskiego modelu repozytorium stosowanego w PLD. W dużym uproszczeniu wygląda ona tak:
packages
+- trunk
| +- db
| | +- SOURCES
| | | +- patch1.patch
| | | +- patch2.patch
| | +- SPECS
| | +- db.spec
| +- rpm
| +- SOURCES
| | +- patch1.patch
| | +- patch2.patch
| +- SPECS
| +- rpm.spec
+- branches
| +- rpm-4.1.1
| | +- db
| | | +- SOURCES
| | | | +- patch1.patch
| | | | +- patch2.patch
| | | +- SPECS
| | | +- db.spec
| | +- rpm
| | +- SOURCES
| | | +- patch1.patch
| | | +- patch2.patch
| | +- SPECS
| | +- rpm.spec
| +- rpm-4.4.1
| +- db
| | +- SOURCES
| | | +- patch1.patch
| | | +- patch2.patch
| | +- SPECS
| | +- rpm.spec
| +- rpm
| +- SOURCES
| | +- patch1.patch
| | +- patch2.patch
| +- SPECS
| +- rpm.spec
+- tags
+- Ac-rpm-4.4.1-2
+- db
| +- SOURCES
| | +- patch1.patch
| | +- patch2.patch
| +- SPECS
| +- rpm.spec
+- rpm
+- SOURCES
| +- patch1.patch
| +- patch2.patch
+- SPECS
+- rpm.spec
Jak widać na powyższym schemacie dla każdego pakietu została wydzielona osobna gałąź w drzewie repozytorium. Taki zabieg, poza uporzadkowaniem i separacją składników poszczególnych pakietów, pozwala na rozdzielenie przestrzeni nazw plików w pakietach. A co za tym idzie, powoduje że np. nazwa patcha może być dowolna dla zadanego pakietu i nie istnieje konieczność pilnowania żeby nazwy łatek dla różnych pakietów nie kolidowały ze sobą. W szczególności, nie ma potrzeby narzucania nazewnictwa tych łatek do postaci nazwa_pakietu-nazwa_łatki.patch jak ma to miejsce w PLD.
Ściagnięcie całego drzewa trunk może wygląda nastepująco:
svn checkout http://svn.pld-freebsd.org/svn/packages/trunk packagesNatomiast ściagniecie i kompilacja pakietu db z takiego repozytorium wygląda tak:
$ cd ~/packagesPowyższy przykład nie uwzględnia oczywiście ściagnięcia ewentualnych tarballi z distfiles.
$ svn checkout http://svn.pld-freebsd.org/svn/packages/trunk/db
$ cd db/SPECS
$ rpmbuild -ba --define "_topdir ~/packages/db" db.spec
Drzewiastej strukturze repozytorium można zarzucać niemozliwość "przegrepowania" wszystkich specy, albo wprowadzania masowych zmian w specach. Wadę tą można wyeliminować używając polecenia find. I tak odpowiednikiem grep jakisstring * w katalogu SPECS jest
find . -name "*.spec" -exec grep -H jakisstring {} \;
w katalogu packages/trunk. Praca z takim repozytorium nie jest dużo bardziej skomplikowana, niż z płaskim repozytorium CVSowym. A prawie zupełnie nie różni się gdy użyjemy odpowiednio zmodyfikowanego skryptu builder.
Kolejną przewagą nad CVSem są "atomowe" commity. Polega to na tym, że po wprowadzeniu zmian z pakiecie, zmiany są przekazywane do repozytorium jednym poleceniem, np.
$ cd ~/packagesCo najważniejsze, takie dokonanie zmiany jest traktowane w repozytorium jako integralna całość identyfikowana numerem rewizji w repozytorium, a nie jest złożeniem zmian w poszczególnych plikach. Co za tym idzie możemy łatwo odszukać, że np. update db do wersji 4.5 pociągneło za sobą usuniecie pliku SOURCES/patch1.patch. W CVSie, niestety, każda tego typu zmiana jest w repozytorium traktowana osobno, dlatego dużo wiecej pracy kosztuje odnalezienie jak zmieniły się pozostałe pliki pakietu, podczas określonej zmiany pliku spec. Takie zachowanie CVSa jest dla mnie szczególnie uciążliwe, ponieważ w swojej pracy intensywnie śledzę zmiany w repozytorium PLD w celu nanoszenia niektórych z nich w PLD/FreeBSD.
$ svn commit -m "- updated to 4.5\n- updated patch1 patch" db
Atomowość commitów w repozytorium pociaga za sobą również zwiekszenie przejrzystości commitlogów, dzięki temu są one generowane jako pojedynczy mail, a nie dwa osobne maile zawierajace zmiany odpowiednio, w module SPECS i w module SOURCES.
Kolejna przewagą Subversion jest możliwość przenoszenia plików (z zachowaniem historii zmian) z poziomu użytkownika. W CVSie taka operacja wymaga zaangażowania administratora repozytorium, który musi dokonać takiej zmiany po stronie serwera.
Dla niektórych kolejną wadą SVNa może być niemożliwość generowania sekcji %changelog, tak jak ma to miejsce w chwili obecnej w PLD. Według mnie używanie $Log$ do generowania loga to średnio dobry pomysł, tym bardziej, że taki log nie jest tworzony w formacie przyjmowanym przez RPMa. Słuszniej byłoby gdyby %changelog był generowany z loga SVNa na source-builderach w formacie natywnym dla RPMa.
Na koniec wypada jeszcze wspomnieć o łatwości instalacji serwera Subversion. Po raz pierwszy zajeło mi to kilkanaście minut, podczas gdy na instalację serwera CVSu straciłem kilka(naście) godzin.