dotnet core: второй подход к снаряду

Written by elwood

Полгода назад я впервые попробовал .NET Core. В тот раз даже запустить helloworld не получилось, но это потому что я запускал его на Ubuntu 15.10, а формально поддерживалась только 14.04. С тех пор прошло немало времени, вышел официальный релиз 1.0 (в котором разработчики отпилили поддержку --native). Добавилась документация на официальном сайте. И я подумал – а почему бы и не попробовать ещё разок ? Пробовать решил на Console Framework. В принципе, это несложный проект, состоящий из нескольких модулей. Внешних зависимостей у него нет.

Установка .NET Core

Не вызвала проблем. Ставил на Ubuntu 14.04 по официальному гайду.

Отдельные модули

Для начала я взялся за те модули, которые не зависят от других. Для того, чтобы .NET Core собрал модуль, нужно, чтобы рядом с кодом модуля лежал файл project.json. Надо где-то получить шаблон этого файла. Я сделал 2 пустых проекта: один командой dotnet new, другой командой dotnet new -t Lib. Почему-то в них оказались разными ссылки на дефолтные библиотеки. Для исполняемого модуля было указано “netcoreapp1.0”:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable",
    "emitEntryPoint": true
  },
  "dependencies": {},
  "frameworks": {
    "netcoreapp1.0": {
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        }
      },
      "imports": "dnxcore50"
    }
  }
}

А для библиотеки “netstandard1.6”

{
  "version": "1.0.0-*",
  "buildOptions": {
    "debugType": "portable"
  },
  "dependencies": {},
  "frameworks": {
    "netstandard1.6": {
      "dependencies": {
        "NETStandard.Library": "1.6.0"
      }
    }
  }
}

Чтобы разобраться, что это за наборы библиотек и чем они отличаются, я сходил и почитал документацию, и ещё чуть-чуть. К сожалению, понимания это не добавило. Я просто попробовал оба набора, и в netstandard1.6 вообще ничего не завелось, а netcoreapp всё-таки получилось заставить работать. В общем, взял тот кусок “frameworks”, который про netcoreapp1.0.

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

При сборке были ошибки. Например, почему-то отсутствовал метод Type.GetCustomAttributes(). Как выяснилось, в .NET 4.5 добавили промежуточный класс TypeInfo, и теперь надо дёргать Type.GetTypeInfo().GetCustomAttributes() – это новый extension-метод в System.Reflection. Аналогично пришлось поменять вызовы IsGenericType, IsEnum, GetEnumNames и GetEnumValues.

Assembly.Load(string assemblyName) пришлось заменить на AssemblyLoad(new AssemblyName(string assemblyName)).

Пропал ArrayList (который не-generic). Пришлось везде поменять на List<object>.

Пропал CharSet.Auto, заменил на CharSet.Unicode (это в интеропе).

А ещё пропал метод Assembly.GetExecutingAssembly(). Теперь его следует вызывать через typeof(SomeTypeInAssembly).GetTypeInfo().Assembly.

Environment.PlatformID пропал совсем (хз что делать, закостылил пока через условную компиляцию).

Не было обнаружено также свойство EventWaitHandle.SafeWaitHandle (тоже удалил через условную компиляцию, т.к. это был windows-specific код).

А ещё пропал класс ApplicationException. Не то, чтобы я сильно по нему скучал (по факту наличие таких исключений само по себе является проблемой в коде), но как-то все эти траублы не добавляют радости. Код действительно приходится портировать, несовместимостей в API очень много.

Многомодульный проект

Когда получилось добиться успешной сборки двух не зависящих ни от чего модулей, я приступил к настройке третьего модуля, который использует первые два. Почему-то в официальном гайде указано, что в родительском каталоге нужно завести файл global.json, однако это совершенно не нужно. Всё работает и без него. Нужно лишь явно указать название и версию каждому модулю в их project.json (“name” и “version”), а в project.json зависящего от них модуля прописать их в dependencies:

  "dependencies": {
    "Binding": {
	"version": "1.0.0",
	"target": "project"
    },
    "Xaml": {
	"version": "1.0.0",
	"target": "project"
    }
  }

После этого при попытке собрать этот проект dotnet автоматически будет собирать и зависимости.

entry points

Осталось настроить сборку ещё одного модуля – собственно исполняемое приложение. Тут поджидала ещё одна проблема. У меня этот модуль содержал несколько классов Program, в каждой из которых был свой метод Main(). В mono и обычном .net можно было спокойно указать класс, который использовался бы как EntryPoint. В .net core почему-то эта штука вообще не работает. По документации, можно определить название метода, однако судя по всему, эта директива вообще игнорируется. Пришлось просто исключить все “лишние” файлы с точками входа:

"exclude": [
  "RadioButtons/**",
  "TreeView/**",
  "Commands/**",
  "AsyncUIUpdate/**",
  "TabControl/**",
  "MainMenu/**",
  "CheckBoxes/**"
],

Заодно узнал, как прописывать embedded ресурсы:

"resource": [
  "**/*.xml"
],

После всех этих манипуляций проект наконец собрался и даже запустился (когда я подложил libtermkey.so), однако пока рано радоваться:

dotnetcore-consoleframework

Ничего не работает, похоже, что с interop’ом в .NET Core большие проблемы.

Кстати, забавно, что dotnet build не создает exe-файлы для исполняемых модулей, делает только dll.

Документация

Документация очень плоха. Официальные гайды слабы. Взять например project.json reference. Дефолтные значения атрибутов не указаны (а в исходной версии этой доки они были!), часть информации устарела. В сети тоже много всего разного. Часто найденные рецепты противоречат друг другу. И не очень понятно, где искать инфу про то, что есть в библиотеке, а чего нет. Был бы нормальный декомпилятор .NET под линукс, было бы проще, но его, похоже, нет. Встроенный в MonoDevelop декомпилятор не справляется (просто не может открыть сборку). Вкупе с другими многочисленными проблемами, всё это делает меня грустить. Я уже не говорю о тулинге (чтобы не пришлось руками редактировать json-файлики). В общем, пока использовать .NET Core рановато. Буду ждать очередных релизов и выхода Rider.

В завершение прикладываю diff изменений (в отдельной ветке), чтобы можно было посмотреть примеры файлов project.json.

  • Andrzej

    Что там с .NET Core 3.0, заработало на нём?