Як мені найкраще керувати створенням відкритих вихідних кодів із конфіденційного дослідницького коду моєї компанії?


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. Чи може бути спосіб безпечно зв’язати сховища разом, щоб ми могли ефективно реінтегрувати наші покращення з версіями з відкритим кодом? Поради щодо інших підводних каменів вітаються.


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')

Я впевнений, що є більш елегантні способи зробити це, але це швидко і брудно, і, здається, вдається виконати роботу.


Нічого, дякую. Можливо, є багато логіки, щоб зробити хороший фільтр, і я ціную ваш приклад. Я сподіваюся, що я знайду щось для повторного використання, і моя машина для розробки швидко працює з великою пам’яттю, тому продуктивність не дуже велика проблема запускати окремі фільтри для авторських прав та визначень, або запускати фільтр визначення не один раз. Насправді у нас є кілька визначень, пов’язаних із ключовими словами, які позначають кілька майбутніх проектів, і пару минулих проектів, які не випускатимуться з відкритим кодом, але все ще використовуються внутрішньо та шляхом раннього прийняття клієнтів.
DeveloperDon

3

Я думав про передачу вашого коду через препроцесор, щоб лише розширити макроси, вивівши таким чином лише цікаву частину в #ifdefs.

Щось подібне повинно працювати:

gcc -E yourfile.c

Але:

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

Можливо, існує спосіб обмежити розширення макросів; однак моя пропозиція тут полягає в тому, щоб розділити речі, а не робити (потенційно небезпечну) обробку файлів (до речі, як би ви планували їх підтримувати? Наприклад, повторно ввести код із версії open source у ваше закрите джерело?).

Тобто постарайтеся якомога більше розмістити код, який ви хочете відкрити у зовнішніх бібліотеках, а потім використовуйте їх, як і в будь-якій іншій бібліотеці, інтегруючись з іншими "спеціалізованими" бібліотеками із закритим кодом.

Спочатку може знадобитися трохи більше часу, щоб зрозуміти, як реструктурувати речі, але це, безумовно, правильний спосіб досягти цього.


Я подумав, чи може бути щось, що можна зробити з препроцесором, щоб вибірково усунути блоки, які ми поки не випустимо. Код є складним, і, ймовірно, нам знадобиться більше коментарів, а не менше, але ваше пропозиція, безумовно, варто включити до списку мозкових штурмів. Питання 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

Примітка. Очевидно, вам потрібно буде розібратися у способі встановлення вхідних / вихідних файлів, але це не повинно бути занадто складним.

Розкриття: Я є оригінальним автором піпрепроцесора.


Убік: Спочатку я написав це як рішення проблеми з технічним обслуговуванням python 2k / 3x. Мій підхід полягав у тому, щоб зробити 2 і 3 розробки в одних і тих же вихідних файлах і просто включити / виключити відмінності, використовуючи директиви препроцесора. На жаль, я виявив важкий шлях, що неможливо написати справжній чистий (тобто не вимагає c) препроцесора в python, оскільки помилки синтаксису лексерів у нестабільному коді помилки, перш ніж препроцесор отримає шанс запуску. У будь-якому випадку, він все ще корисний при широкому спектрі обставин, включаючи вашу.


Це скелі. Якщо ніщо інше, ми могли б зробити щось подібне на тристоронні розробки, які обробляли файли з кодом і без нього, який ми хотіли виключити, взяли їх diff, а потім видалили розрізнені рядки з оригіналу.
DeveloperDon

@DeveloperDon Так, це загальна ідея. Існує кілька різних способів вирішити це, це залежить від того, як ви плануєте керувати циклом фіксації-випуску. Цей твір просто автоматизує багато роботи, яка в іншому випадку була б нудною та / або схильною до помилок.
Еван Плейс

1

Напевно, це було б добре

1.add теги коментарів, як:

> // *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 справляється з цим дуже добре.

Таким чином, ви зберігаєте дві підтримувані копії бази даних. А випустити його для відкритого коду легко як пиріг.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.