Russian Qt Forum
Ноябрь 10, 2024, 22:47 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Провайдеры  (Прочитано 6626 раз)
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« : Сентябрь 12, 2020, 12:17 »

Не всё же Igors'у вопросы задавать. Если из темы ничего не выйдет, не беда, форум уточкой поработает.

Итак, сперва контекст. В Qbs у нас есть модули, от них зависит ваша цель сборки (продукт). Модуль может быть чем угодно - языком программирования, фреймворком, библиотекой. Примеры - cpp, Qt.core. Модули инжектят в продукт разные свойства, например флаги компиляции или библиотеки к которым надо линковаться.

Также есть высокоуровневая абстракция - модуль провайдеры, которые позволяют генерить модули. Например, провайдер "Qt" спрашивает у qmake какие модули установлены и генерит только их.

Когда в коде пишется Depends { name: "foo" } Qbs сперва ищет "реальный" модуль "foo", если такового нет, то она ищет провайдер foo и пробует создать модуль им, если и такового нет, то используется "fallback" провайдер, который спрашивает у утилиты pkg-config пакет "foo".

У провайдеров можно задавать различные свойства, например чтобы подсказать где искать qmake, надо сделать так:
Код:
qbs module-providers.Qt.qmakeSearchPaths:/opt/bin
.
В файловой системе файлы провайдеров лежат примерно так:
Код:
<qbs search path>/module-providers/Qt/provider.qbs
<qbs search path>/module-providers/__fallback/provider.qbs

Теперь ближе к вопросу.
Я хочу разрешить описывать несколько провайдеров для одного модуля для того чтобы поддерживать разные "источники" - помимо pkgconfig хочу добавить Conan, vcpkg и manual (ручной, где пути к любам надо вбивать ручками) провайдеры. Для этого можно завести новый тип MetaProvider у которого будет (пока) одно свойство - wantedProvider (или список, wantedProviders) - тот провайдер, который мы хотим использовать.
В файловой системе это будет выглядеть теперь так (ну разве что meta-provider.qbs можно звать просто provider.qbs, зачем плодить сущности?):
Код:
<qbs search path>/module-providers/zlib/meta-provider.qbs // MetaProvider { wantedProvider: "conan" }
<qbs search path>/module-providers/zlib/pkgconfig.qbs // Provider { ... }
<qbs search path>/module-providers/zlib/conan.qbs // Provider { ... }
<qbs search path>/module-providers/zlib/manual.qbs // Provider { ... }

Аналогично, fallback провайдер выглядит так
Код:
<qbs search path>/module-providers/__fallback/meta-provider.qbs // MetaProvider { wantedProvider: "conan" }
<qbs search path>/module-providers/__fallback/pkgconfig.qbs // Provider { ... }
<qbs search path>/module-providers/__fallback/conan.qbs // Provider { ... }
<qbs search path>/module-providers/__fallback/manual.qbs // Provider { ... }
Это позволит избежать копи-пасты как в примере выше - pkgconfig может жить только в папочке __fallback потому что единственное его свойство - имя пакета - можно вычислить по дефолту из имени желаемого модуля (спойлер - они совпадают). Ну типа в редких случаях (когда они не совпадают, скажем "zlib" vs "libz") придется описать явно, ну не суть, это делали.

Собственно вопрос в том как синтаксически задавать свойства всего этого зоопарка. Раньше (напомню) мы писали module-providers.<provider-name>.property:value (e.g. module-providers.Qt.qmakeSearchPaths:/opt/bin).
Теперь у нас есть разные уровни провайдеров, есть "главный" и есть "вложенные", "дочерние". Ну, допустим:
Код:
module-providers.zlib.wantedProvider:pkgconfig // задаем свойство мета провайдера
module-providers.zlib.pkgconfig.static:true // задаем свойство конкретного провайдера
Окей, с конкретным модулем разобрались, но ведь неудобно для каждой библиотеки перечислять всё это, нужен "глобальный" синтаксис который ставит значения по умолчанию. Ну допустим:

Код:
module-providers.wantedProvider:pkgconfig // глобально задаем свойство мета провайдера
module-providers.pkgconfig.static:true // глобально задаем свойство конкретного провайдера

Опта, коллизия!
Код:
module-providers.zlib.wantedProvider:pkgconfig
module-providers.pkgconfig.static:true
Эти две записи имеют синтаксис module-providers.<foo>.<bar>:baz, а значит потенциально влекут кучу проблем (ну например теперь внутренние провайдеры не могут называться как модули)

Собственно, как решить? Проблема тут чисто в именовании, в остальном схема выглядит весьма продуманно.
Например, такое именование (в предыдущей итерации где была другая, но похожая, схема) решает проблему:
Код:
packages.zlib.wantedSource:pkgconfig // локальный синтаксис
packages.zlib.sources.pkgconfig.static:true // локальный синтаксис
packages.wantedSource:pkgconfig // глобальный синтаксис
packages.sources.pkgconfig.static:true // глобальный синтаксис
Внимательный читатель возмутится - что за пакеты, что за источники? Их больше нет, теперь это MetaProvider и просто Provider. Если мы обобщим схему и вместо "sources" будем использовать "providers" то получается известная субстанция:
Код:
module-providers.wantedProviders:pkgconfig // глобальный синтаксис
module-providers.providers.pkgconfig.static:true // глобальный синтаксис
Провайдеры? - Провайдеры - Провайдеры! (напевать на мотив developers! developers! developers!)
В общем, я сломал всю голову, никак цветок каменный не выходит. Вроде, схема красивая, но есть нюансы.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #1 : Сентябрь 12, 2020, 13:38 »

Я все-же не понял, а в чем разница между:

Цитировать
module-providers.zlib.wantedProvider:pkgconfig

и

Цитировать
module-providers.wantedProvider:pkgconfig

? Первый - ищет zlib.. ну а второй для чего?

И вообще, непонятны эти нововведения (без 100 грамм ну никак)..

Можешь еще на примерах "реальных" проекта (например в сем же zlib) разобрать на пальцах?
Записан

ArchLinux x86_64 / Win10 64 bit
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #2 : Сентябрь 12, 2020, 14:24 »

Я все-же не понял, а в чем разница между

Допустим, у нас проект зависит от нескольких библиотек (zlib, lzma, boost.spirit). И мы хотим использовать Conan при сборке. Тогда мы можем написать
Код:
qbs module-providers.zlib.wantedProvider:conan module-providers.lzma.wantedProvider:conan module-providers.boost.spirit.wantedProvider:conan 

Это слишком многословно и такая гранулярность не нужна (точнее нужна, но в редких кейзах когда 1 модуль ищется через pkgconfig, а второй через Conan, ну потому что аффтары либы не написали .pc файл и pkg-config просто нету). Поэтому хочется уметь задавать значение "глобально", для всех искомых модулей сразу, например, так:
Код:
module-providers.wantedProvider:conan

Ну или (тот редкий случай) "по умолчанию" conan но для lzma - pkgconfig:
Код:
module-providers.wantedProvider:conan module-providers.lzma.wantedProvider:pkgconfig

Цитировать
И вообще, непонятны эти нововведения (без 100 грамм ну никак)..

Хе-хе, мне тоже=) Это далеко не первая версия, возможно третья. Идея в том чтобы позволять выбирать разные "источники" для библиотек. Сейчас поддерживается только один - pkg-config, но его нету на венде, вендузятники страдают.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #3 : Сентябрь 12, 2020, 15:30 »

Цитировать
Это слишком многословно и такая гранулярность не нужна

А почему бы не сократить доступ еще, например так:

Цитировать
qbs module-providers:conan module-providers.lzma:conan module-providers.boost.spirit:conan
qbs module-providers:conan
qbs module-providers:conan module-providers.lzma:pkgconfig

т.е. убрать 'wanted' ?

Так, а теперь приведи пример с самой проблемой, я не понял, в чем она?  Улыбающийся

В чем тут коллизия?

Цитировать
module-providers.zlib:pkgconfig                // Задаем свойство провайдера zlib искать zlib через pkgconfig.
module-providers.zlib:pkgconfig.static:true // Задаем свойство провайдера zlib искать только статическую zlib через pkgconfig.

module-providers:pkgconfig                     // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска (тут какие то его дефолтные настройки, например динамик по умолчанию).
module-providers:pkgconfig.static:true      // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска статических библиотек.

Тут по умолчанию глобально используется pkgconfig и ищет, например, динамик либы по умолчанию. Но мы переопределяем ему св-во static=true, и теперь он ищет только статик либы глобально для всех через pkgconfig.

Но для zlib смысла ставить static нету, т.к. оно итак ищет статику. Мы могли бы для zlib установить:

Цитировать
module-providers.zlib:pkgconfig.static:false

и тогда бы оно искало только динамические либы... И могли бы опустить вот это:

Цитировать
module-providers.zlib:pkgconfig                // Задаем свойство провайдера zlib искать zlib через pkgconfig.

вообще, т.к. глобально оно и так использует pkgconfig.

Или я не понял?
« Последнее редактирование: Сентябрь 12, 2020, 15:57 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #4 : Сентябрь 12, 2020, 15:53 »

Например, можем "переиграть" (вот это ниже должно вызывать ошибку):

Цитировать
module-providers.zlib:conan.static:true // Задаем свойство провайдера zlib искать только статическую zlib через conan (но мы еще не указали конана для zlib, а уже лезем менять его св-ва).

module-providers:pkgconfig                 // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска

т.к. тут коллизия, для zlib мы не установили дефолтного провайдера 'conan' и попытались присвоить свойство к еще несуществующему конану.

Вот так бы не было ошибки:

Цитировать
module-providers.zlib:conan                // Задаем свойство провайдера zlib искать zlib через conan (типа создаем локального конана для zlib).
module-providers.zlib:conan.static:true // Задаем свойство провайдера zlib искать только статическую zlib через conan.

module-providers:pkgconfig                 // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска

Тут для всех остальных модулей будет юзаться pkgconfig (ищущий что-то с дефолтными настройками, например, динамические либы), а вот для zlib будет использоваться только conan и линковать статикой.
« Последнее редактирование: Сентябрь 12, 2020, 15:59 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #5 : Сентябрь 12, 2020, 16:30 »


А почему бы не сократить доступ еще, например так:

Ну потому что там могут появиться еще проперти. Я уже даже знаю одну - надо ли делать fallback, то есть искать в папке __fallback. Типа MetaProvider (MultiplexProvider, Package, you name it) выглядит так

Код:
Item { // обе проперти "встроенные", такого базового типа нет. Но можно наследовать Provider (не вижу зачем)
    property string wantedProvider: "pkgconfig"
    property bool fallback: true
}

Код:
qbs module-providers:conan module-providers.lzma:conan module-providers.boost.spirit:conan 
qbs module-providers:conan
qbs module-providers:conan module-providers.lzma:pkgconfig
Вообще, парсер так устроен что он складывает prefix.a.b.c.prop:value в мапу вот так: map["a.b.с"]["prop"] == value. Префикс не может быть пустой, иначе все остальные ключи (вида modules.foo.bar.baz) полетят в эту мапу. Второй ключ (a.b.c) - может быть пустым, то есть "module-providers.wantedProviders" попадёт в map[""]["wantedProviders"]. Prefix сейчас может иметь значения "product", "modules" или "module-providers", соответсвенно оно кладется в 3 разные мапы. Можно добавить новый префикс, если что - см. пример с "packages." выше.

Так, а теперь приведи пример с самой проблемой, я не понял, в чем она?  Улыбающийся
В чем тут коллизия?

Код:
module-providers.zlib:pkgconfig                // Задаем свойство провайдера zlib искать zlib через pkgconfig. 
module-providers.zlib:pkgconfig.static:true // Задаем свойство провайдера zlib искать только статическую zlib через pkgconfig.

module-providers:pkgconfig                     // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска (тут какие то его дефолтные настройки, например динамик по умолчанию).
module-providers:pkgconfig.static:true      // Глобально задаем свойство для всех провайдеров использовать pkgconfig для поиска статических библиотек.

Ну так работать не будет, см выше. Я думал о добавлении сложного синтаксиса типа module-providers.zlib[pkgconfig].static:true, но это надо чтобы парсер QML поддерживал это. Плюс кмк это плохая идея - gyp пошел по этому пути и там было всякое разное разыменование переменных типа <(foo), >(foo), <@(foo), >@(foo) и это имело разное значение. Так себе подход, без пол-литры не запомнишь.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #6 : Сентябрь 12, 2020, 16:37 »

Например, можем "переиграть" (вот это ниже должно вызывать ошибку):



А вот тут будут возмущаться некие немцы. В идеале, wantedProvider превратиться в список wantedProviders и тебе надо будет ставить проперти для нескольких провайдеров сразу. Ну и это надо уже сейчас, например ты можешь захардкодить эти проперти в проекте/продукте сразу:
Код:
Product {
    Depends { name: "zlib" }
    module-providers.zlib.pkgconfig.static:true
    module-providers.zlib.conan.static:true
}
а выбор провайдера делать с консоли:
Код:
qbs module-providers.wantedProvider:pkgconfig

Самая засада что сейчас ни у pkgconfig ни у manual провайдеров своих пропертей сейчас нет (пример со static синтетический, скорее всего оно будет не так), но синтаксис придумать надо - они могут внезапно появиться, скажем у Conan.
« Последнее редактирование: Сентябрь 12, 2020, 16:40 от Авварон » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #7 : Сентябрь 12, 2020, 16:48 »

Ну что пока я придумал (не буду Игорсом который размусолит на неделю, напишу сразу) - не выделять "главный" (мета) провайдер синтаксически и указывать его явно:

Код:
// local syntax
module-providers.zlib.main.wantedProviders:pkgconfig
module-providers.zlib.pkgconfig.static:true
module-providers.zlib.conan.static:true
// global syntax
module-providers.main.wantedProviders:pkgconfig
module-providers.pkgconfig.static:true
module-providers.conan.static:true
Тут есть небольшая неконстистентность - айтем зовется MetaProvider, лежит в папке под именем provider.qbs, а обращение к нему через "main". Хрен запомнишь. Обозвать его Package? Вроде неплохо, запомнить что Package - это мета-информация для выбора провайдеров (и всякая другая) вроде не сложно. Тогда вроде консистентно (ну кроме факта что Package лежит рядом с провайдерами но с этим можно жить, наверное).
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #8 : Сентябрь 12, 2020, 16:57 »

А если просто "опускать" main, при этом считаем, что доступ к "корневому" провайдеру тогда будет?

Цитировать
// local syntax
module-providers.zlib.wantedProviders:pkgconfig
module-providers.zlib.pkgconfig.static:true
module-providers.zlib.conan.static:true
// global syntax
module-providers.wantedProviders:pkgconfig
module-providers.pkgconfig.static:true
module-providers.conan.static:true

?

Да и wantedProviders слово не нравится. Может заменить на что-то например на factories/sources?

Записан

ArchLinux x86_64 / Win10 64 bit
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #9 : Сентябрь 12, 2020, 17:08 »

Цитировать
// local syntax
package.zlib.providers:pkgconfig (но нельзя установить 'global' и переопределить его св-ва из локального, считаем ошибкой)
provider.zlib.pkgconfig.static:true
provider.zlib.conan.static:true
// global syntax
package.{global|default}.providers:pkgconfig
provider.{global|default}.pkgconfig.static:true
provider.{global|default}.conan.static:true
« Последнее редактирование: Сентябрь 12, 2020, 17:10 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #10 : Сентябрь 15, 2020, 14:16 »

А если просто "опускать" main, при этом считаем, что доступ к "корневому" провайдеру тогда будет?

Код:
// local syntax
module-providers.zlib.wantedProviders:pkgconfig
module-providers.zlib.pkgconfig.static:true
module-providers.zlib.conan.static:true
// global syntax
module-providers.wantedProviders:pkgconfig
module-providers.pkgconfig.static:true
module-providers.conan.static:true

Ну так тут коллизия - это ты как различаешь случаи
module-providers.zlib.wantedProviders:pkgconfig
module-providers.pkgconfig.static:true
а синтаксически оба случая module-providers.a.prop:value. Как понять, а - имелся ввиду модуль а или провайдер а?

Да и wantedProviders слово не нравится. Может заменить на что-то например на factories/sources?

Да можно просто providers если не будет коллизий с другими свойствами.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #11 : Сентябрь 15, 2020, 14:22 »

Мне пришла идея в голову, почему это не засунуть в продукт/depends?
Вот так писать неудобно, потому что приходится повторять себя (DRY!)
Код:
Product {
    Depends { name: "Qt.core"; providers: ["pkgconfig"] }
}

Поэтому можно сделать, чтобы providers брало значение из аналгичного свойства Продукта, а то - из аналогичного свойтва Проекта
Код:
Project {
    providers: ["pkgconfig"] // global
    Product {
        name: "a"
        Depends { name: "Qt.core"; }
    }
    Product {
        providers: ["conan"]  // product override
        name: "b"
        Depends { name: "Qt.core"; }
    }
    Product {
        name: "c"
        Depends { name: "Qt.core"; providers: ["manual"] } // depends override
    }
}

Тогда можно просто писать
Код:
qbs projects.rootProject.providers:pkgconfig
"Глобально" устанавливать свойства для определенного модуля, можно так (страшненько, да)
Код:
Project {
    providersPerModule: {
         "Qt.core": "pkgconfig"
         "Qt.gui": "manual"
    }
}
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #12 : Май 17, 2021, 17:54 »

Апну темку Улыбающийся

Сейчас я пытаюсь решить проблему совместимости со старым кодом.

Как провайдеры работают сейчас - если Qbs не может найти модуль, скажем, Qt.core, она идет и ищет провайдер "Qt.core", если такого нет, то ищет "Qt", который и создает утешные модули. Таким образом, если проект не зависит от Qt.core, то провайдер не вызывается.

Теперь, мы делаем новое свойство qbsModuleProviders: ["Qt", "conan", "pkgconfig"], которое задает порядок, в котором выполняются провайдеры. Сначала работает "Qt", который создает модули для Qt, потом "conan", потом "pkgconfig". Бонусом имеем то, что разные провайдеры могут создавать одноименные модули, но приоритет имеют те, которые раньше в списке - например, если и "conan", и "pkgconfig" предоставляют модуль "zlib", то будет использован от "conan" - очень удобно делать механизм фоллбека между провайдерами.

Также теперь необязательно выполнять провайдеры, когда модуль не найден, можно выполнить весь список ДО поиска модулей - это упростит код в Qbs.
Однако появляется проблема - как быть с дефолтами? Если сделать значение qbsModuleProviders по умолчанию пустым, то сломается автопоиск Qt и pkgconfig. Правда во втором всё равно ничего нет, так что и не проблема=) но вот с Qt это проблема.
Какие варианты есть?
1а. Если qbsModuleProviders пустой, то можно попробовать выполнить ВСЕ провайдеры, которые есть. Это плохо, так как это ужасно медленно (сетап Qt где-то полсекунды, pkg-config 4 секунды чтобы сгенерить все возможные модули, но тут можно оптимизировать).
1б. То же, что 1а, но провайдер говорит, надо ли его звать по дефолту (enabledByDefault: true). Это позволит выключить особо тяжелые провайдеры, но остается проблема - если проект не зависит от Qt, а у Qt провайдера enabledByDefault: true, то юзеру будет непонятно, почему мы пытаемся сетапить Qt
1в. То же, что 1б, но enabledByDefault вычисляется, скажем, по наличию qmake в PATH. Проблему особо не решает, но если проверять, задано ли свойство провайдера qmakeSearchPaths (оно задаётся при запуске qbs setup-qt), то это решит совместимость при использовании Qt-профиля - если юзер явно указал Qt профиль, то наверное он хочет Qt. Но это не решит проблему при автодетекте qmake в PATH - независимо от наличия qmake в PATH мы не знаем заранее, нужна ли юзеру Qt.
2. Ничего не делать и заставить юзера указывать qbsModuleProviders руками в проекте:
Код:
QtApplication {
    Properties {
         condition: qbs.version >= 1.21
         qbsModuleProviders: ["Qt", "conan", "pkgconfig"]
    }
}
Ломается поведение по умолчанию, но можно пока оставить старые реализации провайдеров Qt и pkg-config и при попытке их использовать, выдавать warning - мол, deprecated, задавай qbsModuleProviders явно.
В целом, явное задавание имеет смысл, потому что провайдеры могут создавать не только плюсовые/кутешные модули и, скажем, делать там ["pkgconfig"] по дефолту для Java или там Typescript проекта не имеет смысл.

Какие мысли?
« Последнее редактирование: Май 17, 2021, 18:24 от Авварон » Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.116 секунд. Запросов: 23.