Grzegorz Dubicki (adastra)

Zarządzanie i rozrywka w IT - blog w wersji RC ;)

poniedziałek, 25 luty 2008

Czy GPL to licencja wirusowa i czy warto się spierać z prawnikami

23 lutego na poznańskim Barcampie #6 Krzysztof Jarosiński i Marcin Błaszyk z Kancelarii Prawnej Renata Urowska i Wspólnicy sp.k. przedstawili prezentację pt. „Zjawisko „open source software” oraz rodzaje licencji stanowiących podstawę jego rozpowszechniania. Problem „zarażania” oprogramowania tworzonego w firmach licencjami typu open source”.

(Slajdy z prezentacji oraz pewnie nagranie znajdzie się lada dzień na barcamp.pl. Dołączę tu linka do nich, gdy to nastąpi.)


Po prezentacji, podczas części przeznaczonej na dyskusję, zwróciłem prelegentom uwagę, że moim zdaniem:

0. sieją nieuzasadniony niepokój dotyczący open source'ie, aby mieć więcej pracy w kancelarii przy rozstrzyganiu kwestii prawnych [na potrzeby firm, które mogłyby tak naprawdę rozwiązywać samodzielnie, mając kogoś obdarzonego zdolnością logicznego myślenia i umiejętnością czytania ze zrozumieniem w języku angielskim],

1. GPL (w domyśle wszędzie dalej mówimy o wersji 2 tej licencji) wcale nie wymusza rozdawania programu znajdującego się na tej licencji za darmo, tzn. nie zakazuje sprzedaży po cenie wyższej niż 0 / koszt nośnika. Poprosiłem ich o wskazanie w jakim konkretnie miejscu GPL zakazuje sprzedaży oprogramowania na tej licencji.

2. stwierdzenie padające na w ostatnim punkcie, 19-go slajdu tej prezentacji, mówiące że:
(cytuję na ile pamiętam - zamieszczę dokładny cytat gdy slajdy znajdą się na barcamp.pl) wykorzystanie narzędzi na licencji GPL do tworzenia strony WWW spowoduje, że kod tej strony "zarazi" się licencją GPL i trzeba będzie ujawnić jej kod,
jest błędne, bo używanie narzędzia na licencji GPL do utworzenia jakiegokolwiek dzieła nie powoduje, że dzieło to "nabywa" GPL. Jako przykład podałem to, że wiele firm używa Linuksa, który jest na licencji GPL i bynajmniej nie powoduje to, że to, co jest wytwarzane za pomocą komputerów działających pod kontrolą tego systemu "nabywa" automatycznie licencję GPL.


Uzyskałem następujące odpowiedzi:

Ad. 0. bez komentarza - dziwne.. ;)

Ad. 1. GPL jednak wymusza, aby oprogramowanie na tej licencji było oddawane za darmo a opłaty można pobierać tylko za usługi,

Dodatkowo w kuluarach Marcin Błaszyk w odpowiedzi na moją prośbę wskazał mi m.in. punkt 2 b) GPL, mówiący że:
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
(...)
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
co w tym momencie przekonało mnie, że się pomyliłem.

Mając jednak nadal wrażenie, że punkt ten jest sprzeczny chociażby z następującym zdaniem z preambuły GPL:
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish)
szukałem przez jakiś czas w sieci głębszych analiz tego zagadnienia - i takie znalazłem w postaci artykułu Marcina Maruty z Kuczek Maruta i Wspólnicy Kancelarii Radców Prawnych pt. "GPL - płatne czy bezpłatne?", z którego kluczowym dla tego problemu jest wg. mnie stwierdzenie:
Jednym słowem – zarabiaj na dystrybucji GPL ile chcesz, byle tylko była to opłata za usługę, a nie za prawo do korzystania z programu. O zakazie pobierania opłat licencyjnych świadczy (wprost lub a contrario) kilka punktów licencji GPL jak chociażby ostatnie zdanie pkt. 1 You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee, pkt 2b You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License czy pkt. 11 because the program is licensed free of charge, there is no warranty for the program, to the extent permitted by applicable law. Tam, gdzie mowa o prawie do opłat our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish) mowa jest o dystrybucji.
- cóż, to chyba mówi samo za siebie i rozstrzyga nasz mały prawniczy spór w tej kwestii na korzyść kolegów z Kancelarii Urowska ale zachęcam do lektury całego wyżej wspomnianego artykułu. Myślę, że zaspokaja ewentualną potrzebę zgłębienia tego tematu przez nieprawnika w około 100% a jednocześnie nie jest za długi i zbyt wnikający w prawnicze niuanse.

Ad. 2. W rozmowie w kuluarach Marcin Błaszczyk przyznał, że za to ten punkt slajdu prezentacji był pewnym uproszczeniem i że chodziło np. o korzystanie przy tworzeniu stron z frameworków na licencji GPL.

No oczywiście zmienia postać rzeczy, bo tu bez wahania, nawet nie będąc prawnikiem mogę potwierdzić, że taki sposób użycia programu na licencji GPL, jak włączenie kodu tego programu w kod własnego programu, powoduje że nasz program staje się dziełem pochodnym od programu na licencji GPL, co powoduje że musimy go potem dystrybuować na licencji nie innej niż GPL.

..ale czy naprawdę nie ma ryzyka, że dzieło utworzone za pomocą narzędzia na licencji GPL zostaje GPLem "zarażone"?

To też jednak postanowiłem zbadać głębiej (czy dokładniej: poszukać wiarygodnych opinii innych na ten temat, bo sam nie jestem jednak prawnikiem i chyba w tej sytuacji rozsądniej jest podeprzeć się opinią prawnika), z powodu uwagi na stronie (takim tradycyjnym sensie a nie na stronie WWW ;) innego gościa Barcampu (którego nazwiska nie pamiętam), który powiedział, że program, którego postać binarna jest generowana przy użyciu kompilatora lub interpretera kodu źródłowego do wynikowego, który to kompilator lub interpreter jest dystrybuowany na licencji GPL, też może stać się programem na licencji GPL.

Zaniepokoiło mnie, że być może jest większa grupa ludzi, która tak myśli i postanowiłem znaleźć dowód, że nigdy program na licencji GPL nie zaraża swoich dzieł.

Ale niestety taki przypadek miał już miejsce!

Otóż cytując FAQ dotyczący licencji GPL :

Q: Can I use GPL-covered editors such as GNU Emacs to develop non-free programs? Can I use GPL-covered tools such as GCC to compile them?

A: Yes, because the copyright on the editors and tools does not cover the code you write. Using them does not place any restrictions, legally, on the license you use for your code.

Some programs copy parts of themselves into the output for technical reasons—for example, Bison copies a standard parser program into its output file. In such cases, the copied text in the output is covered by the same license that covers it in the source code. Meanwhile, the part of the output which is derived from the program's input inherits the copyright status of the input.

As it happens, Bison can also be used to develop non-free programs. This is because we decided to explicitly permit the use of the Bison standard parser program in Bison output files without restriction. We made the decision because there were other tools comparable to Bison which already permitted use for non-free programs.

Szczerze mówiąc po takim precedensie (że pozwolę sobie użyć terminu prawniczego :) zacząłem się obawiać, że inne narzędzia GPLowe również mogą włączać fragmenty swojego kodu do kodu wynikowego.

Bo czy aby na pewno nie robi tego np. żaden z kompilatorów z rodziny GCC? Dla żadnej platformy? Np. kawałka kodu w asemblerze?

Generalnie myślę, że nie, głowy bym za to już teraz nie dał.


Moje wnioski:
  • nie warto publicznie spierać się z prawnikami w dyskusji pt. "kto ma rację", bo można się boleśnie pomylić..
  • ..chociaż przynajmniej w ten sposób można poszerzyć ostatecznie swoją wiedzę..
  • ..a jeśli napiszę się o tym na blogu, to może nie tylko swoją. :)

środa, 27 grudzień 2006

Thinking (too hard) in Java?

Dostałem w tym roku pod choinkę "Thinking in Java" Bruce'a Eckela'a - wydanie IV, edycję polską.

Oczywiście bardzo się ucieszyłem, i to nie tylko dlatego, że dostałem ją od Ukochanej, która pamiętała, że już dłuższy czas temu głośno wzdychałem przy niej w księgarni, że to "Biblia programistów Java" i że "bardzo chciałbym ją mieć".

Przede wszystkim dlatego, że naprawdę zawsze tak o tej książce myślałem.

Po części z powodu entuzjastycznej opinii na jej temat przekazywanej na Uczelni przez doktora M. (..a może mu za to płacą? ;) a trochę z "poczytywania" jej w zawsze za małych dawkach w bibliotece i u znajomych oraz fragmentów w wersji elektronicznej, do tej pory myślałem, w uproszczeniu, że można zauważyć prostą prawidłowość - im lepiej ktoś zna tę książkę, tym jest lepszym programistą Java. Jest to teza dość naiwnie brzmiąca gdy się ją napisze, ale testy znajomości języka Java, przeprowadzane przy okazji rekrutacji programistów w różnych firmach, sugerują, że nie tylko ja byłem zdania, że znajomość najbardziej zaawansowanych elementów języka i biblioteki Java SE/EE jest ostatecznym wyznacznikiem poziomu kompetencji programisty Java.

Ponieważ do tej pory uważałem też siebie za dość zaawansowanego programistę kodującego w tym języku, zacząłem czytać bardziej zaawansowane rozdziały by powiększyć i ugruntować swoją wiedzę na ich temat, dla swojej przyjemności, na potrzeby swoich aktualnych i przyszłych projektów i przyszłych rekrutacji (niekoniecznie w tej kolejności ;).

I na podstawie tych kilku godzin lektury (..oraz może trochę tych łącznie już paruletnich projektów, które realizowałem do tej pory pełniąc różne role, ale ostatecznie zwykle będąc za nie odpowiedzialnym jako kierownik całości), stwierdzam, że to nie jest takie proste i że nie ma jednego kryterium tego, czy programista "dobry".

Korzystając z tego, że piszę to u siebie i na swoją odpowiedzialność postawię następującą tezę: są 2 podstawowe grupy programistów Java, których określiłbym mianem "dobrych"- "solidnych koderów" i "wymiataczy programistycznych".

"Solidnych koderzy", wg. mojej-i-tylko-mojej terminologii, niekoniecznie znają na pamięć zaawansowane aspekty klas wewnętrznych i anonimowych czy tajniki optymalnego programowania współbieżnego, ale za to szybko piszą ładny kod, który działa choć niekoniecznie szybko.

Pod pojęciem "ładny" rozumiem tu:
  1. zgodność ze standardem formatowania kodu Suna
  2. odokumentowanie kodu Javadocami
  3. korzystanie z logowania Commons Logging/Log4J/innego ustalonego standardu
  4. itd. - inne zasady tego poziomu stosowane w projekcie (Macie jakieś konkretne typy?)
"Działa" oznacza zaś ni mniej ni więcej to, że kod ma robić to, co jest określone w specyfikacji dla danej klasy czy metody.

Niekoniecznie jednak kod wygenerowany przez takich ludzi ma być optymalny! Dokładniej: nie powinien być, jeśli optymalizacja miałaby się odbyć kosztem szybkość kodowania (dokładne granice należałoby ustalać oczywiście per projekt, lub może per programista, na przykładach). Do optymalizacji najlepsi są...

"Wymiatacze programistyczni". To (wg. tej roboczej terminologii) programiści, którzy potrafią pisać optymalny kod dla konkretnych problemów.

Niekoniecznie muszą go ładnie komentować, można im wybaczyć niezbyt szybkie tempo tworzenia kodu a nawet inne, pozainformatyczne "fochy". Ich kod jest jednak jak najbliższy doskonałości sam w sobie i na potrzeby tworzenia go znają oni najbardziej zaawansowane aspekty języka i używanych bibliotek oraz solidnie algorytmikę i kwestie złożoności obliczeniowej.

Jestem zdania, że zwykle pełne powodzenie niespecjalistycznych, ogólnobiznesowych projektów wymaga udziału programistów z obu grup.

"Koderzy biznesowi" są fundamentem etapu tworzenia wersji "beta", pierwszych implementacji nowych funkcjonalności. W 1-szej iteracji (przy pracy w metodologiach iteracyjnych) wystarczy zespół złożony z programistów tylko tego typu.

"Wymiatacze programistyczni" są zaś niezbędni na etapie kodowania wersji "finalnych", przy pracy nad produkcyjną implementacją realizowaną w kolejnych iteracjach dla rzeczy już wstępnie zakodowanych w iteracjach wcześniejszych.

- tak pokrótce przedstawia się ta moja robocza teoria (być może zbieżna z jakąś, którą wymyślił ktoś inny, lecz ja doszedłem do niej niezależnie), do której doszedłem nad kartami "TIJ".

Ale gdzie tutaj ślad "TIJ"? - pewnie już pytacie. Otóż cała powyższa refleksja była zapoczątkowana lekturą poniższego fragmentu. Jest to począek rozdziału 18-go, traktującego o IO. (Zacząłem czytać go, bo we wstępie Eckel zapowiada wyjaśnianie dalej historii rozwoju części biblioteki Java SE odnoszącej się do IO, co przekładać ma się na jej głębsze zrozumienie.) Eckel zaczyna od ogólnego opisu klasy File. Podaje przykład kodu wypisującego za pomocą File zawartość katalogu:

//: io/DirList.java
// Display a directory listing using regular expressions.
// {Args: "D.*\.java"}
import java.util.regex.*;
import java.io.*;
import java.util.*;

public class DirList {
public static void main(String[] args) {
File path = new File(".");
String[] list;
if(args.length == 0)
list = path.list();
else
list = path.list(new DirFilter(args[0]));
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem);
}
}

class DirFilter implements FilenameFilter {
private Pattern pattern;
public DirFilter(String regex) {
pattern = Pattern.compile(regex);
}
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
} /* Output:
DirectoryDemo.java
DirList.java
DirList2.java
DirList3.java
*///:~
Kod interfejsu FilenameFilter:
public interface FilenameFilter {
boolean accept(File dir, String name);
}
Dalej jest wyjaśnienie, co robi ten kod, które teraz pominę bo jest to raczej oczywiste.

Istotne tu jest coś innego: Eckel robi w tym momencie wtręt z anonimową klasą wewnętrzną:
Ten przykład nadaje się świetnie do przepisania z użyciem anonimowej klasy wewnętrznej (opisanej w rozdziale "Klasy wewnętrzne"). W pierwszej kolejności tworzona jest metoda filter() zwracająca referencję do FilenameFilter:
//: io/DirList2.java
// Uses anonymous inner classes.
// {Args: "D.*\.java"}
import java.util.regex.*;
import java.io.*;
import java.util.*;

public class DirList2 {
public static FilenameFilter filter(final String regex) {
// Creation of anonymous inner class:
return new FilenameFilter() {
private Pattern pattern = Pattern.compile(regex);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
}; // End of anonymous inner class
}
public static void main(String[] args) {
File path = new File(".");
String[] list;
if(args.length == 0)
list = path.list();
else
list = path.list(filter(args[0]));
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem);
}
} /* Output:
DirectoryDemo.java
DirList.java
DirList2.java
DirList3.java
*///:~
Zauważmy, że argument dla metody filter() musi być typu final. Jest to wymagane przez anonimową klasę wewnętrzną, by mogła ona użyć obiektu spoza własnego zasięgu. Taki sposób zaprojektowania jest ulepszeniem, ponieważ klasa FilenameFilter jest teraz ściśle związana z DirList2. Można również posunąć się krok dalej i zdefiniować anonimową klasę wewnętrzną jako argument dla list() - w tym przypadku jest nawet mniejsza.
//: io/DirList3.java
// Building the anonymous inner class "in-place."
// {Args: "D.*\.java"}
import java.util.regex.*;
import java.io.*;
import java.util.*;

public class DirList3 {
public static void main(final String[] args) {
File path = new File(".");
String[] list;
if(args.length == 0)
list = path.list();
else
list = path.list(new FilenameFilter() {
private Pattern pattern = Pattern.compile(args[0]);
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
});
Arrays.sort(list, String.CASE_INSENSITIVE_ORDER);
for(String dirItem : list)
System.out.println(dirItem);
}
} /* Output:
DirectoryDemo.java
DirList.java
DirList2.java
DirList3.java
*///:~
Teraz argument przekazywany do main() jest typu final, ponieważ anonimowa klasa wewnętrzna korzysta wprost z args[0].

Przykład ten pokazuje zastosowanie anonimowych klas wewnętrznych do tworzenia unikalnych klas "jednorazowego użycia" służących do rozwiązywania napotkanych problemów. Korzyścią jest zgromadzenie kodu, który służy do rozwiązania konkretnego problemu, odizolowanego w jednym miejscu. Z drugiej strony, uzyskane klasy są dosyć nieczytelne, należy więc używać go rozważnie.
Teraz zagadka: czy chodzi mi o to, że 1-szy przykład z File jest dla/w stylu "Solidnych koderów biznesowych" a dalsze dla "Wymiataczy programistycznych"?

Otóż wcale nie! Eckel pisze, że daje to:
(...) zgromadzenie kodu, który służy do rozwiązania konkretnego problemu, odizolowanego w jednym miejscu.
- teoretycznie tak, ale jak mogłoby dojść do wygenerowania takiego kodu?
  1. "Wymiatacz" napisałby coś takiego od razu.
  2. Ktoś kazałby ponownie zaimplementować "wymiataczowi" kod 1-szy napisany przez "kodera"
Ten 1-szy przypadek, moim zdaniem, oznacza błąd w zarządzaniu, polegający na tym, że "wymiataczowi" zleca się implementację kodu realizującego daną funkcjonalność jako pierwszemu, zamiast dać mu do zoptymalizowania kod wygenerowany przez "kodera".

2-gi zaś oznaczałby błąd w realizacji zadania, bo "wymiatacz" pisząc kod który nie jest znacząco szybszy niż kod "naiwny" nie zrealizuje poprawnie swojego zadania, które polega na implementacji kodu zoptymalizowanego.

(Oczywiście tak to wygląda tylko przy założeniu, że trzymamy się mojej teorii "wymiataczy" i "koderów". Tak generalnie oczywiście rozumiem, że Eckel podał w tym miejscu ten kod w celach edukacyjnych, jako jakiś praktyczny przykład zastosowania klasy wewnętrznych i klas anonimowych i patrząc na niego z tego punktu widzenia, uwzględniając podsumowanie z ostatniego zacytowanego w tym poście akapitu, nie mam do niego zastrzeżeń.)

Etykiety: , , , ,