Как мне лучше всего управлять выпуском открытого исходного кода из конфиденциального исследовательского кода моей компании?

13

Моя компания (назовем их Acme Technology) имеет библиотеку из примерно тысячи исходных файлов, которые первоначально были получены от исследовательской группы Acme Labs, инкубированы в группе разработчиков в течение пары лет и совсем недавно были предоставлены нескольким клиентам в не разглашать. Acme готовится выпустить, возможно, 75% кода для сообщества с открытым исходным кодом. Остальные 25% будут выпущены позже, но на данный момент либо не готовы к использованию заказчиком, либо содержат код, связанный с будущими инновациями, которые они должны держать в руках конкурентов.

Код в настоящее время отформатирован с помощью #ifdefs, который позволяет той же кодовой базе работать с подготовительными платформами, которые будут доступны для университетских исследователей и гораздо более широкого круга коммерческих клиентов, когда он перейдет на открытый исходный код, и в то же время является доступны для экспериментов, создания прототипов и прямого тестирования совместимости с будущей платформой. Хранение единой базы кода считается важным для экономики (и здравомыслия) моей группы, которой было бы нелегко поддерживать две копии параллельно.

Файлы в нашей текущей базе выглядят примерно так:

> // Copyright 2012 (C) Acme Technology, All Rights Reserved.
> // Very large, often varied and restrictive copyright license in English and French,
> // sometimes also embedded in make files and shell scripts with varied 
> // comment styles. 
> 
> 
>   ... Usual header stuff...
>
> void initTechnologyLibrary() {
>     nuiInterface(on);
> #ifdef  UNDER_RESEARCH
>     holographicVisualization(on);
> #endif
> }

И мы хотели бы преобразовать их в нечто вроде:

> // GPL Copyright (C) Acme Technology Labs 2012, Some rights reserved.
> // Acme appreciates your interest in its technology, please contact xyz@acme.com 
> // for technical support, and www.acme.com/emergingTech for updates and RSS feed.
> 
>   ... Usual header stuff...
>
> void initTechnologyLibrary() {
>     nuiInterface(on);
> }

Есть ли инструмент, библиотека синтаксического анализа или популярный скрипт, который может заменить авторские права и удалить не только #ifdefs, но и такие варианты, как #if определенные (UNDER_RESEARCH) и т. Д.?

Код в настоящее время находится в Git и, вероятно, будет размещен где-то, где используется Git. Был бы способ безопасно связать репозитории вместе, чтобы мы могли эффективно интегрировать наши улучшения с версиями с открытым исходным кодом? Консультации о других подводных камнях приветствуются.

DeveloperDon
источник
13
Эта кодовая база кричит о ветвях.
Флориан Маргэйн
Пример использования веток для этой цели был бы очень кстати.
DeveloperDon

Ответы:

6

Кажется, что не составит труда написать сценарий для анализа препроцессоров, сравнения их со списком определенных констант ( UNDER_RESEARCH, FUTURE_DEVELOPMENTи т. Д.) И, если директива может быть оценена как ложная, учитывая то, что определено, удалить все к следующему #endif.

В Python я бы сделал что-то вроде

import os

src_dir = 'src/'
switches = {'UNDER_RESEARCH': True, 'OPEN_SOURCE': False}
new_header = """// GPL Copyright (C) Acme Technology Labs 2012, Some rights reserved.
// Acme appreciates your interest in its technology, please contact xyz@acme.com 
// for technical support, and www.acme.com/emergingTech for updates and RSS feed.
"""

filenames = os.listdir(src_dir)
for fn in filenames:
    contents = open(src_dir+fn, 'r').read().split('\n')
    outfile = open(src_dir+fn+'-open-source', 'w')
    in_header = True
    skipping = False
    for line in contents:
        # remove original header
        if in_header and (line.strip() == "" or line.strip().startswith('//')):
            continue
        elif in_header:
            in_header = False
            outfile.write(new_header)

        # skip between ifdef directives
        if skipping:
            if line.strip() == "#endif":
                skipping = False
            continue
        # check
        if line.strip().startswith("#ifdef"):
            # parse #ifdef (maybe should be more elegant)
            # this assumes a form of "#ifdef SWITCH" and nothing else
            if line.strip().split()[1] in switches.keys():
                skipping = True
                continue

        # checking for other forms of directives is left as an exercise

        # got this far, nothing special - echo the line
        outfile.write(line)
        outfile.write('\n')

Я уверен, что есть более изящные способы сделать это, но это быстро и грязно и, кажется, делает свою работу.

WasabiFlux
источник
Вау, спасибо. Существует много логики для создания хорошего фильтра, и я ценю ваш пример. Я надеюсь найти что-то для повторного использования, и моя машина для разработки работает быстро с большой памятью, поэтому производительность не является большой проблемой для запуска отдельных фильтров для авторского права и определений или для запуска фильтра определения более одного раза. На самом деле у нас есть несколько определений, связанных с ключевыми словами, которые обозначают несколько будущих проектов и пару прошлых проектов, которые не будут выпущены с открытым исходным кодом, но все еще используются внутри компании и ранними пользователями.
DeveloperDon
3

Я думал о том, чтобы передать ваш код через препроцессор, чтобы расширить только макросы, выводя только интересную часть в #ifdefs.

Примерно так должно работать:

gcc -E yourfile.c

Но:

  • Вы потеряете все комментарии. Вы можете использовать -CCих (вроде) для сохранения, но тогда вам все равно придется удалить старое уведомление об авторских правах.
  • #includes также расширены, так что вы получите большой файл, содержащий все содержимое включенных заголовочных файлов
  • Вы потеряете «стандартные» макросы.

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

То есть попробуйте как можно больше поместить код, в котором вы хотите открыть исходный код, во внешние библиотеки, а затем использовать их так же, как и в любой другой библиотеке, интегрируя с другими «пользовательскими» библиотеками с закрытым исходным кодом.

Поначалу может потребоваться немного больше времени, чтобы понять, как что-то реструктурировать, но это определенно верный способ сделать это.

redShadow
источник
Я подумал, можно ли что-то сделать с препроцессором, чтобы выборочно устранить блоки, которые мы еще не выпустим. Код сложен, и нам, вероятно, понадобится больше комментариев, а не меньше, но ваше предложение, безусловно, стоит включить в список мозгового штурма. Вопросы WRT о том, как мы планируем поддерживать исходный код и передавать код назад и вперед сообществу, необходимо больше планирования. Внедрение кода в проприетарный код вызывает некоторые хорошие вопросы.
DeveloperDon
2

У меня есть решение, но оно потребует небольшой работы

pypreprocessor - это библиотека, которая предоставляет чистый препроцессор в стиле c для python, который также может использоваться в качестве GPP (препроцессора общего назначения) для других типов исходного кода.

Вот основной пример:

from pypreprocessor import pypreprocessor

pypreprocessor.input = 'input_file.c'
pypreprocessor.output = 'output_file.c'
pypreprocessor.removeMeta = True
pypreprocessor.parse()

Препроцессор очень прост. Это делает проход через источник и условно комментирует источник, основываясь на том, что определено.

Определения могут быть установлены либо с помощью операторов #define в источнике, либо путем установки их в списке pypreprocessor.defines.

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

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

Примечание: Обычно это не нужно устанавливать явно, потому что python автоматически удаляет закомментированный код во время компиляции в байт-код.

Я вижу только один крайний случай. Поскольку вы хотите предварительно обработать источник C, вы можете явно задать определения процессора (например, через pypreprocessor.defines) и сказать ему игнорировать операторы #define в источнике. Это должно предотвратить случайное удаление любых констант, которые вы можете использовать в исходном коде вашего проекта. В настоящее время нет параметров для установки этой функциональности, но добавить ее было бы тривиально.

Вот тривиальный пример:

from pypreprocessor import pypreprocessor

# run the script in 'production' mode
if 'commercial' in sys.argv:
    pypreprocessor.defines.append('commercial')

if 'open' in sys.argv:
    pypreprocessor.defines.append('open')

pypreprocessor.removeMeta = True
pypreprocessor.parse()

Тогда источник:

#ifdef commercial
// Copyright 2012 (C) Acme Technology, All Rights Reserved.
// Very large, often varied and restrictive copyright license in English and French,
// sometimes also embedded in make files and shell scripts with varied 
// comment styles.
#ifdef open
// GPL Copyright (C) Acme Technology Labs 2012, Some rights reserved.
// Acme appreciates your interest in its technology, please contact xyz@acme.com 
// for technical support, and www.acme.com/emergingTech for updates and RSS feed.
#endif

Примечание: очевидно, вам нужно будет выбрать способ установки файлов ввода / вывода, но это не должно быть слишком сложным.

Раскрытие: я являюсь первоначальным автором pypreprocessor.


За исключением: я изначально написал это как решение проблемы обслуживания python 2k / 3x. Мой подход заключался в том, чтобы делать 2 и 3 разработку в одних и тех же исходных файлах и просто включать / исключать различия, используя директивы препроцессора. К сожалению, я обнаружил сложный способ, которым невозможно написать настоящий чистый (то есть не требующий c) препроцессор в python, потому что лексер помечает синтаксические ошибки в несовместимом коде до того, как препроцессор получит возможность работать. В любом случае, это все еще полезно в широком диапазоне обстоятельств, включая ваши.

Эван Плейс
источник
Это качается. Если бы не что-то еще, мы могли бы сделать что-то вроде трехстороннего сравнения, которое обрабатывало файлы с и без кода, который мы хотели исключить, брало их diff, а затем удаляло переведенные строки из оригинала.
DeveloperDon
@DeveloperDon Да, это общая идея. Есть несколько разных способов справиться с этим, это зависит от того, как вы планируете управлять циклом коммита-релиза. Эта часть просто автоматизирует большую часть работы, которая в противном случае была бы утомительной и / или подверженной ошибкам.
Эван Плейс
1

Вероятно, было бы неплохо

1. добавить комментарий теги, как:

> // *COPYRIGHT-BEGIN-TAG*
> // Copyright 2012 (C) Acme Technology, All Rights Reserved.
> // Very large, often varied and restrictive copyright license in English and French,
> // sometimes also embedded in make files and shell scripts with varied 
> // comment styles. 
> // *COPYRIGHT-ENG-TAG*
>   ... Usual header stuff...
>
> void initTechnologyLibrary() {
>     nuiInterface(on);
> #ifdef  UNDER_RESEARCH
>     holographicVisualization(on);
> #endif
> }

2. Напишите скрипт для компоновщика с открытым исходным кодом, который просматривает все файлы и заменяет текст между тегами COPYRIGHT-BEGIN-TAG и COPYRIGHT-ENG-TAG.

Алекс Хашими
источник
1
Нужен ли тег начала? Пока все наши исходные файлы начинаются с авторских прав в первой строке, а наши сценарии оболочки начинаются с авторских прав во второй строке. Есть много файлов, поэтому я хотел бы сделать как можно меньше ручного редактирования.
DeveloperDon
Я думаю, что некоторые файлы могут использовать Doxygen для определения имен своих функций, параметров и возвращаемых значений. Для тех файлов, которые еще не были настроены таким образом, было бы действительно много редактирования, если бы мы сделали выбор, который продолжил бы в этом направлении.
DeveloperDon
По крайней мере, вы должны изменить его один раз. если ваша политика в области авторских прав изменилась, вы можете управлять ею.
Алекс Хашими
1

Я не собираюсь показывать вам инструмент для преобразования вашей базы кода, множество ответов уже сделали это. Скорее, я отвечаю на ваш комментарий о том, как обрабатывать ветви для этого.

У вас должно быть 2 филиала:

  • Сообщество (давайте назовем версию с открытым исходным кодом, как это)
  • Профессиональный (давайте назовем версию с закрытым исходным кодом, как это)

Препроцессоры не должны существовать. У вас есть две разные версии. И более чистая кодовая база в целом.

Вы боитесь поддерживать две копии параллельно? Не волнуйся, ты можешь слиться!

Если вы вносите изменения в ветку сообщества, просто объедините их с профессиональной веткой. Git справляется с этим очень хорошо.

Таким образом, вы сохраняете 2 поддерживаемые копии вашей кодовой базы. И выпустить один для открытого источника легко, как пирог.

Флориан Маргейн
источник