none
Как получить текст C# после обработки директив условной компиляции? RRS feed

  • Вопрос

  • Вопрос возник такой - наша фирма разрабатывает ПО разным потребителям, соответственно используются одинаковые модули для разных покупателей. Вопрос решается применением условной компиляции. Сейчас необходимо представить одному из заказчиков исходный код, но при этом так, чтобы от УК там не было и упоминания. Понятно, что руками можно вычистить, но есть библиотеки, в которых количество #if исчисляется 100-ми и даже превышает 1500!

    Я верю, что должен быть нормальный способ откомпилировать код под заказчика и получить текст уже без директив УК. Я попробовал заменять значимые имена ключей на true или false (искал с регулярными выражениями) - это быстрее, чем чистка, но метод не подходит, т.к. лишние фрагменты остаются... Должно быть что-то еще! Может быть какие-то ключи компилятора..?

    Visual Studio 2013, 2015.

    27 июня 2017 г. 6:45

Ответы

  • Это работает:

    Завел отдельную папку cl (место произвольное). В ней создал папку 1033 и положил туда файл clui.dll. Сама папка cl содержит файлы: c1.dll, cl.exe, mspdb140.dll. Все это я вычислил по "просьбам" компилятора при попытке выполнить команду: cl -E SourceFile.cs > test.cs, где SourceFile.cs - это произвольный файл из проекта в котором есть условная компиляция.

    Да - сработала - на выходе получил файл без условной компиляции, но я не понимаю, по какому ключу УК он работает? Он точно вычисляет блоки УК, но по какому-то ключу по умолчаанию.

    Теперь - как задать ключи УК? Я использовал параметр D: cl -DKey1 -DKey2 SourceFile.cs > test.cs - равноценно обработке ключей УК в настройках свойств в VS: Key1;Key2

    Наверное вопрос решен. Если есть другие способы - пишите, буду рад о них узнать.


    • Изменено Denis Prokofjev 27 июня 2017 г. 9:08
    • Помечено в качестве ответа Denis Prokofjev 27 июня 2017 г. 9:08
    27 июня 2017 г. 8:19

Все ответы

  • Прогоните через препроцессор C++.

    This posting is provided "AS IS" with no warranties, and confers no rights.

    27 июня 2017 г. 6:49
    Модератор
  • Спасибо, Илья.

    Я нашел Препроцессор такую статью, только в ней про c/c++. Значит работает и для c#, спасибо. А где его найти? :) Что это за файл?

    27 июня 2017 г. 6:58
  • Не пойму, как это сделать. Расскажите поподробнее, если это настройки в VS, то где и какие, если запуск отдельного файла в командной строке - то, что за файл и какие ключи?

    C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\cl.exe - это он? Для VS 2015.

    Если он, то с ключами понятно /E, но возникает вопрос - должен быть какой-то config (для указания ключей условной компиляции хотя бы), чтобы компиляция вырезала ненужные и оставляла нужные фрагменты. Что это за файл или настройка? Можно пример?

    27 июня 2017 г. 7:18
  • Это работает:

    Завел отдельную папку cl (место произвольное). В ней создал папку 1033 и положил туда файл clui.dll. Сама папка cl содержит файлы: c1.dll, cl.exe, mspdb140.dll. Все это я вычислил по "просьбам" компилятора при попытке выполнить команду: cl -E SourceFile.cs > test.cs, где SourceFile.cs - это произвольный файл из проекта в котором есть условная компиляция.

    Да - сработала - на выходе получил файл без условной компиляции, но я не понимаю, по какому ключу УК он работает? Он точно вычисляет блоки УК, но по какому-то ключу по умолчаанию.

    Теперь - как задать ключи УК? Я использовал параметр D: cl -DKey1 -DKey2 SourceFile.cs > test.cs - равноценно обработке ключей УК в настройках свойств в VS: Key1;Key2

    Наверное вопрос решен. Если есть другие способы - пишите, буду рад о них узнать.


    • Изменено Denis Prokofjev 27 июня 2017 г. 9:08
    • Помечено в качестве ответа Denis Prokofjev 27 июня 2017 г. 9:08
    27 июня 2017 г. 8:19
  • Это стандартный трюк для практически любых файлов - C#, VB, XML - может даже FORTRAN (не пробовал). 

    Да, ключи определяются как обычно для конкретного C++ компилятора. По умолчанию все они не определены.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    27 июня 2017 г. 20:51
    Модератор
  • А все-таки интересно - есть компилятор именно для c# в виде отдельной программы?

    То, что я нашел - cl.exe в общем устраивает, но приходится писать дополнительные обработки файла, чтобы решить вопросы:

    1. компилятор c++ не знает #region и #endregion. Это решается очень просто, например заменой #region на $region и т.п. с последующей обратной заменой в обработанном файле :).

    2. Директива #define работает в C# немного по-другому... точнее директивы #if и #elif. В C# любое из #if или #elif выполнится для дефайна без значения: #define Key1, но вот cl.exe уже ругается - ему требуется, например так: #define Key1 1.

    Я пока нашел только две нестыковки, но может еще будут :). Так существует компилятор для C#, который можно запускать для конкретного файла .CS и который будет понимать все директивы шарпа как надо?

    5 июля 2017 г. 13:05
  • Только не компилятор, а препроцессор. И нет, не слышал о таком специально для C#, всегда использовался C++. Помните что ваша задача очень специфична, обычно это делают совсем с другими целями и такой проблемы не возникает.

    This posting is provided "AS IS" with no warranties, and confers no rights.

    5 июля 2017 г. 22:54
    Модератор
  • Директивы препроцессора в C++ и C# работают абсолютно по разному. В C++ можно написать #define TRUE FALSE и все полетит к черту. В c# они нужны только для обработки условий. Не скармливайте исходные тексты C# компилятору С++, напишите свой парсер. 
  • Тогда попрошу объяснить для чайника, а то совсем запутался. Итак - препроцессор, компилятор... cl.exe - это что? И если это компилятор, то почему он позволяет на выходе получать те же .cs файлы? Или препроцессор и компилятор - это логические блоки (функции) одной программы? Что есть препроцессор и что есть компилятор с точки зрения исполняемых файлов?

    Вадим, я уже понял, что использовать эту программу надо аккуратно, т.к. разница есть, но она пока решаема, а вот писать свой парсер, который бы решал условную компиляцию - честно сказать не очень хочется :). По крайней мере сейчас. Но одно я прояснил - #define в C# используется только для условий, тогда как в C++ был еще сам факт определения ключа и проверка на него #ifdef вроде.

  • Нашел компилятор C#, но легче от этого  не стало - команды по выполнению только условной компиляции, как описывалось выше нет. Может просто не понимаю, как это сделать? Вот его ключи по команде csc.exe -?. Я взял C:\Windows\Microsoft.NET\Framework\v4.0.30319 (для C# 5) 

    This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

                            Параметры компилятора Visual C#

                            - ВЫХОДНЫЕ ФАЙЛЫ -
    /out:<файл>                    Указать имя выходного файла (по умолчанию: базовое имя файла с главным классом или первым файлом)
    /target:exe                    Построить консольный исполняемый файл (по умолчанию) (Краткая форма: /t:exe)
    /target:winexe                 Построить исполняемый файл Windows  (Краткая форма: /t:winexe)
    /target:library                Построить библиотеку (Краткая форма: /t:library)
    /target:module                 Построение модуля, который может быть добавлен в другую сборку (Краткая форма: /t:module)
    /target:appcontainerexe        Построение исполняемого файла Appcontainer (Краткая форма: /t:appcontainerexe)
    /target:winmdobj               Построение промежуточного файла среды выполнения Windows, используемого WinMDExp (Краткая форма: /t:winmdobj)
    /doc:<файл>                    Генерируемый файл XML-документации
    /platform:<строка>             Платформы, на которых может работать этот код: x86, Itanium, x64, arm или anycpu. Платформа по умолчанию: anycpu.

                            - ВХОДНЫЕ ФАЙЛЫ -
    /recurse:<подстановочный знак> Включает все файлы в текущем каталоге и подкаталогах в соответствии с заданным шаблоном
    /reference:<псевдоним>=<файл>  Указывать метаданные из заданного файла сборки, используя данный псевдоним (Краткая форма: /r)
    /reference:<список файлов>     Указывать метаданные из заданных файлов сборок (Краткая форма: /r)
    /addmodule:<список файлов>     Скомпоновать указанные модули со сборкой
    /link:<список файлов>          Внедрять метаданные из указанных файлов сборок взаимодействия (Краткая форма: /l)

                            - РЕСУРСЫ -
    /win32res:<файл>               Задать файл ресурсов Win32 (.res)
    /win32icon:<файл>              Использовать этот значок для вывода
    /win32manifest:<файл>          Укажите файл манифеста Win32 (.xml)
    /nowin32manifest               Не включать манифест Win32 по умолчанию
    /resource:<resinfo>            Внедрить указанный ресурс (Краткая форма: /res)
    /linkresource:<resinfo>        Компоновать указанный ресурс вместе с этой сборкой (Краткая форма: /linkres)
                                   Где формат resinfo : <файл>[,<строковое имя>[,public|private]]

                            - ГЕНЕРИРОВАНИЕ КОДА -
    /debug[+|-]                    Выдать отладочную информацию
    /debug:{full|pdbonly}          Задает тип отладки (по умолчанию, "full" — полная) и позволяет подключить отладчик к выполняющимся программам
    /optimize[+|-]                 Включить оптимизацию (Краткая форма: /o)

                            - ОШИБКИ И ПРЕДУПРЕЖДЕНИЯ -
    /warnaserror[+|-]              Обрабатывать все предупреждения как ошибки
    /warnaserror[+|-]:<список предупреждений>
                                   Обрабатывать указанные предупреждения как ошибки
    /warn:<n>                      Установить порог предупреждений (0-4) (Краткая форма: /w)
    /nowarn:<список предупреждений>Отключить указанные предупреждения

                            - ЯЗЫК -
    /checked[+|-]                  Сгенерировать проверки переполнений
    /unsafe[+|-]                   Допускать "небезопасный" код
    /define:<список символов>      Определить символ(ы) условной компиляции (Краткая форма: /d)
    /langversion:<строка>          Укажите режим версии языка: ISO-1, ISO-2, 3, 4, 5 или Default

                            - БЕЗОПАСНОСТЬ -
    /delaysign[+|-]                Использовать отложенную подпись для сборки, используя только открытую часть ключа строгого имени
    /keyfile:<файл>                Указать файл ключа для строгого имени
    /keycontainer:<строка>         Указать контейнер ключа для строгого имени
    /highentropyva[+|-]            Включение ASLR в высокой энтропией

                            - РАЗНОЕ -
    @<файл>                        Считывает файл ответов с дополнительными параметрами
    /help                          Отображает это сообщение об использовании (Краткая форма: /?)
    /nologo                        Запрещает отображение сообщения компилятора об авторских правах
    /noconfig                      Не включать файл CSC.RSP автоматически

                            - ДОПОЛНИТЕЛЬНО -
    /baseaddress:<адрес>           Базовый адрес библиотеки, которая будет построена
    /bugreport:<файл>              Создать файл отчета об отладке
    /codepage:<n>                  Задать кодовую страницу для использования при открытии исходных файлов
    /utf8output                    Выводит сообщения компилятора в кодировке UTF-8
    /main:<тип>                    Задать тип, который содержит точку входа (все остальные возможные точки входа игнорируются) (Краткая форма: /m)
    /fullpaths                     Компилятор генерирует полные пути
    /filealign:<n>                 Задает границы для секций выходных файлов
    /pdb:<файл>                    Укажите имя файла  отладочной информации (по умолчанию: имя результирующего файла с расширением .pdb)
    /errorendlocation              Вывод строки и столбца конечного расположения каждой ошибки
    /preferreduilang               Укажите имя предпочтительного языка вывода.
    /nostdlib[+|-]                 Не обращаться к стандартной библиотеке (mscorlib.dll)
    /subsystemversion:<строка>     Укажите версию подсистемы этой сборки
    /lib:<список файлов>           Задает дополнительные каталоги для поиска ссылок
    /errorreport:<строка>          Указать способ обработки внутренних ошибок компилятора: prompt, send, queue или none. По умолчанию используется queue.
    /appconfig:<файл>              Укажите файл конфигурации приложения, содержащий настройки привязки сборки
    /moduleassemblyname:<строка>   Имя сборки, частью которого будет этот модуль

    Есть какие-то предположения, как получить текст .CS, только с уже обработанной УК?

    -------

    PS уже дважды встречаю высказывания такого плана: ... Препроцессор стал частью компилятора (т.е. перестал быть препроцессором в традиционном понимании этого слова)... 

    Т.е. моя задача не может быть решена с помощью компилятора C# - такого функционала просто нет. Можно использовать C++, но с преобразованиями файла или писать что-то свое.

  • cl.exe - компилятор языка c++. Препроцессор - один из этапов компиляции. В с++ он имеет большое значение (и опасность, с точки зрения трудноуловимых ошибок), поэтому выделена возможность отдельного его запуска. В c# его возможности ограничены, поэтому такого не предусмотрели. 

    Если от этого у вас зависит бизнес-процесс, лучше наверно потратить время и написать обработчик директив самому (или взять с open source реализаций C#).