7 дек. 2015 г.

Лицензии (L)GPLv2, (L)GPLv3 и MPL2

Лицензии (L)GPL и MPL2 относятся к copyleft-лицензиями (иногда их также называют "strict licenses").

В отличие от permissive-лицензий (MIT, BSD, Apache), они вводят дополнительные ограничения. В целом эти ограничения обязывают того, кто распространяет модифицированный код, публиковать те или иные части своих исходников, но детали различаются.

Текст ниже - это мое понимание, так что он может содержать ошибки.

TL;DR


Предположим, что есть библиотека или программа foo, опубликованная под одной из лицензий (L)GPL или MPL2, а вы пишете библиотеку или приложение prog.

Упрощенное резюме такое:
  • Если foo под GPLv2:
    • если prog содержит модифицированную версию foo или статически или динамически линкуется с foo, prog должна публиковаться под лицензией, совместимой и сохранающей требования GPLv2 (permissive-лицензии не являются таковыми);
    • если prog использует foo через IPC или system(), ограничений на prog не накладывается.
  • Если foo под LGPLv2:
    • если prog динамически линкуется с foo, ограничений на prog не накладывается;
    • если prog статически линкуется с foo, и предоставляются объектные файлы prog для возможности перелинковки с другой версией foo, ограничений на prog не накладывается;
    • в остальных случаях действуют правила GPLv2.
  • Если foo под GPLv2 with linking exception:
    • если prog динамически или статически линкуется с foo,  ограничений на prog не накладывается;
    • в остальных случаях действуют правила GPLv2.
  • (L)GPLv3 добавляет дополнительные ограничения связанные с патентами/DRM/тивоизацией.
  • Код под GPLv2 и GPLv3 нельзя использовать совместно в одной программе. Код под "GPLv2 or any later version" можно использовать совместно с кодом под GPLv3.
  • Если foo под MPL2:
    • prog должна предоставить модифицированные файлы foo под MPL2 или совместимой лицензией;
    • других ограничений на prog не накладывается.

GPLv2


GPLv2 вводит понятие derivative work. Является ли ваша программа prog  derivative work по отношению к foo, нужно выяснять у юриста в каждом конкретном случае, но в целом:

prog является derivative work от foo, если:
  • prog является модифицированной версией foo, или
  • prog включает значительную чать кода foo (копирует или импортирует, например с помощью #include), или
  • prog статически или динамически линкуется с foo.
prog не является derivative work от foo, если:
  • prog запускает foo как внешний процесс (например через system());
  • prog взаимодействует с foo через IPC (например через HTTP API);
  • prog включает только незначительную часть кода foo (например, prog использует объявления структур данных или объявления API из foo, но не использует код и реализацию API из foo).

Эти критерии не перечислены в GPL. Понятие derivative work является общеупотребимым, и, при возникновении спорной ситуации, суд будет решать, является ли работа derivative или нет.

Две лицензии считаются совместимыми, если они не вводят ограничений, не дающих возможности совмещать код под этими двумя лицензиями в одном проекте.

GPLv2 вводит следующее ограничение:
derivative work должна быть опубликована под лицензией, совместимой с GPLv2 и сохраняющей ее требования.

В результате, если foo опубликована под GPLv2, а prog содержит или напрямую использует код из foo (например, линкуется с ней), тогда prog должна быть опубликована под лицензий, совместимой и сохраняющей требования GPLv2.

LGPLv2


LGPLv2 -- это модификация GPLv2. Она вводит дополнительное понятие combined work, являющееся частным случаем derivative work в треминах GPLv2, но не в терминах LGPLv2.

В целом, prog является combined work по отношению к foo,если
  • prog использует немодифицированную версию foo (например статически или динамически линкуется с ней);
  • prog позволяет пользователю подменить версию foo на стороннюю (в случае динамической линковки эта возможность очевидна; в случае статической, автор prog может предоставить объектные файлы, чтобы пользователь мог перелинковать их со своей версией foo).

LGPLv2 вводит следующее ослабление:
combined work не считается derivative work и может быть опубликована под любой лицензией.

В остальных случаях действуют обычные правила GPLv2.

GPLv2 with linking exception


Многие проекты используют текст GPLv2 с добавленным к нему исключением:
если prog статически или динамически линкуется с немодифицированной версией foo, prog не считается derivative work и может быть опубликована под любой лицензией.

Это требование слабее, чем аналогичное в LGPLv2, т.к. не обязывает предоставлять объектные файлы в случае статической линковки.

Примеры таких лицензии: eCos, OpenSSL. Некоторые из таких лицензий официально одобрены FSF (как совместимые с GPL) и OSI (как открытые), поэтому лучше использовать какую-нибудь из них, чем писать исключение самому.

GPLv3 и LGPLv3


GPLv3/LGPLv3 это следующая версия GPL, включающая уточнения и некоторые дополнительные ограничения, например:
  • патенты: разработчик, публикующий софт или derivative work под GPLv3, не может ограничить пользователя с помощью патентов в обход лицензии;
  • DCMA, DRM: разработчику, публикующему софт или derivative work под GPLv3, труднее ввести ограничения на использование софта;
  • тивоизация: разработчик, публикующий софт или derivative work под GPLv3 совместо с аппаратным обеспечением, не может лишить пользователя возможности модифицировать софтовую составляющую продукта;
  • терминология: используется терминология, не привязанная к US;
  • совместимость: внесены правки, делающие GPLv3 совместимым с некотороми другими открытыми лицензями.

Отличия между GPLv3 и LGPLv3 те же, что между GPLv2 и LGPLv2.

Несовместимость GPLv2 и GPLv3


И GPLv2, и GPLv3 разрешают публиковать derivative work только под совместимой лицензией (не накладывающих дополнительных ограничений) и сохраняющей все требования оригинальной лицензии.

Однако:
  • GPLv3 вносит дополнительные ограничения по сравнению с GPLv2, и поэтому не совместима с ней.
  • GPLv2 не сохраняет все требования GPLv3, а значит код под GPLv3 нельзя использовать в проекте под GPLv2.
В результате, программы или библиотеки под лицензиями GPLv2 и GPLv3 нельзя использовать совместно. В случае LGPL или GPL with linking exception, этого ограничения нет, когда в дело вступает исключение, связанное с линковкой.

GPLv2 "or any later version"


Многие проекты под GPLv2 публиковались с формулировкой "GPLv2 or any later version". По сути, это означает что проект публикуется сразу под несколькими лицензиями: GPLv2, GPLv3 и любые версии GPL, выпущенные FSF в будущем.

В этом случае, проект можно использовать совместно как с GPLv2 софтом, так и с GPLv3 софтом.

Однако это также означает, что авторы derivative work могут выбрать любую из лицензий или обе. Если они выберут только GPLv3, то вы не сможете использовать их derivative work совместно с GPLv2. В том числе, вы не сможете влить их изменения в основной проект одновременно под обеими лицензиями GPLv2 и GPLv3. Кроме того, их derivative work будет защищена ограниченями GPLv3, связанными с патентами, тивоизацией, etc.

Передача прав FSF


Некоторые проекты, в дополнение к лицензированию кода под GPL, также передают copyright (авторские права) на код в FSF. Это делается для того, чтобы увеличить юридическую силу лицензии: FSF является зарегистрированной огранизацией и может отстаивать права на код, переданный в FSF, при возникновении нарушений.

Для передачи прав, нужно отправить заявку в FSF, дождаться ответа, подписать его и отправить обратно по бумажной почте или факсу.

См. также gpl-violations.org.

MPL2 


MPL2, в отличие от GPL, является пофайловой лицензией. Она разрешает использовать отдельные файлы любым способом, при условии что эти файлы (и их модифицированные версии) продолжают распространяться под MPL2 или другой открытой лицензией.

Например, если prog линкуется с foo (статически или динамически) или включает в себя некоторые файлы из foo, автор prog должен опубликовать только модифицированные файлы foo, но не prog.

MPL2 является совместимой с (L)GPL. При комбинировании софта под MPL2 и (L)GPL, в случае модификации файлов под MPL2, их нужно перелецензировать под (L)GPL, что явно разрешено MPL2.

GPL и более слабые лицензии


GPL не допускает перепубликацию под permissive-лицензиями, т.к. они не сохраняют требования GPL. Однако permissive-лицензии допускают любое использование, в том числе перепубликацию под GPL:
  • Если foo выпущена под более слабой лицензией, чем GPL (например, MPL2, BSD, MIT), а prog выпущена под GPL, версия foo в составе prog автоматически перелицезируется под GPL. Это требует GPL и разрешено более слабыми лицензиями (явно в MPL2 и неявно в permissive-лицезиях).
  • Если foo выпущена под GPL, использующая ее prog не может быть выпущена под более слабой лицезией, т.к. этого не разрешает GPL.

Если код выпущен под более слабой лицензией, чем GPL, авторы модификаций могут сделать их доступными под более сильной лицензией, вроде GPL.

В этом случае эти модификации нельзя будет использовать в проекте с более слабой лицензией, в частности нельзя будет влить в исходный проект под исходной лицензией.

Копирайты и смена лицензии


Если разработчики не передают права (copyright) на код одному лицу (FSF, компании-владельцу проекта, или одному человеку), тогда единственный легальный способ использовать их результат совместно -- использовать его под той лицензией, под которой выпущен проект и с которой разработчики автоматически согласились при его модификации.

Это значит, что для того, чтобы выпустить проект под другой лицензией, потребуется согласие каждого разработчика, у которого есть права на часть кода.

Выпуск под другой лицензией может понадобится, например, для публикации на AppStore, который накладывает ограничения, несовместимые с (L)GPL, и кроме того явно запрещает софт под (L)GPL.

Некоторые проекты требуют от разработчиков передать права владельцу проекта при внесении изменений в апстрим.

1 дек. 2015 г.

SCons: проблемы с генерацией исходников, VariantDir и SConsign

Проблема


После добавления в проект, использующий VariantDir, генерации исходников и их сборки, происходит следующее:
  • после каждого запуска, scons пересобирает все или некоторые файлы заново, даже если они не изменялись;
  • если удалить файл .sconsign.dblite, scons не может перегенерировать его, как обычно, и падает в конце сборки со следующим трейсом:
OSError: [Errno 2] No such file or directory: '.sconsign.dblite':
  File "/usr/lib64/python2.7/site-packages/SCons/Script/Main.py", line 1372:
    _exec_main(parser, values)
  File "/usr/lib64/python2.7/site-packages/SCons/Script/Main.py", line 1335:
    _main(parser)
  File "/usr/lib64/python2.7/site-packages/SCons/Script/Main.py", line 1099:
    nodes = _build_targets(fs, options, targets, target_top)
  File "/usr/lib64/python2.7/site-packages/SCons/Script/Main.py", line 1297:
    jobs.run(postfunc = jobs_postfunc)
  File "/usr/lib64/python2.7/site-packages/SCons/Job.py", line 113:
    postfunc()
  File "/usr/lib64/python2.7/site-packages/SCons/Script/Main.py", line 1294:
    SCons.SConsign.write()
  File "/usr/lib64/python2.7/site-packages/SCons/SConsign.py", line 109:
    syncmethod()
  File "/usr/lib64/python2.7/site-packages/SCons/dblite.py", line 127:
    self._os_unlink(self._file_name)
Exception OSError: OSError(2, 'No such file or directory') in
  <bound method dblite.__del__ of <SCons.dblite.dblite object at 0x7fa287435c10>> ignored

У меня проблема проявляется на scons 2.3.5 и 2.4.1, но только в составе большого проекта. Воспроизвести вне этого проекта баг не удалось.

Баг


Баг заключается в том, что:
  • по-умолчанию SCons создает отдельные файлы .sconsign для каталога с исходниками (например, ту где лежит SConstruct, но я не знаю от чего это зависит) и для каталога сборки (куда указывает VariantDir);
  • при каком-то стечении обстоятельств, scons записывает информацию о сборке в .sconsign внутри VariantDir, а ищет ее в другом каталоге, и не может найти.
Т.к. SCons не смог найти информацию о предыдущей сборке файла, он считает что файл не был собран. При запуске с --debug=explain можно увидеть следующее:

scons: Cannot explain why `<FILENAME>' is being rebuilt: No previous build information found       

Решение


Чтобы все починить, можно заствить SCons использовать один глобальный файл .sconsign на весь проект. Для этого надо передать абсолютный путь в функцию SConsignFile, например:

import os.path
env.SConsignFile(os.path.join(env.Dir('#').abspath, '.sconsign.dblite'))