Qbs (Qt Build System) — система сборки, позволяющая описывать процесс сборки проектов на простом языке QML (javascript-подобный декларативный язык), ускоряющий процесс сборки продуктов за счет построения подробного графа зависимостей. Хоть эта система и создана разработчиками Qt, но она жестко не привязана к Qt и позволяет собирать любые продукты, для любых языков программирования и даже использоваться не для программирования, а например для администрирования. Как заявлено в официальной документации:
A product is the target of a build process, typically an application, library or maybe a tar ball

Сегодня и рассмотрим процесс создания своих продуктов. Поехали…

Знакомство с это системой сборки хорошо описал mapron в данной статье, мы же не будем на этом останавливаться и приступим сразу к делу.

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

Итак Qbs выполняет преобразования одних данных в другие с помощью элемента Rule. Данный элемент создает правило преобразования, которое преобразует входные данные inputs в выходные outputs при помощи набора команд, созданных в скрипте prepare. Каждая команда может как сама выполнять преобразования, так и делегировать эту работу внешней программе.

Входные артефакты


В качестве входных артефактов для правила преобразования выступают файлы собираемого продукта с тегом указанным в свойстве inputs, а также артефакты полученные из зависимостей собираемого продукта с тегом указанным в свойстве inputsFromDependencies (неявным является тег "installable", который присваивается всем файлам которые необходимо проинсталлировать, т.е. qbs.install: true).

Например:

---product1.qbs---
Application {
    name: "simpleApplication";
    targetName: "appSimple"
    files: ["main.cpp", "app.h", "app.cpp"]
    Depends {name: "cpp"}
}

---product2.qbs---
Product {
    type: "exampleArtefact"
    name: "example"
    targetName: "myArtefact"
    Depends {name: "simpleApplication" }
    Depends {name: "exampleModule" }
    Group {
        files: ["description.txt", "readme.txt"]
        fileTags: ["txtFiles"]
    }
    Group {
        files: ["imgA.png", "imgB.png", "imgC.png"]
        fileTags: ["pngFiles"]
    }
    Group {
        files: ["fontA.ttf", "fontB.ttf"]
        fileTags: ["fontFiles"]
    }
}

---exampleModule.qbs---
Module {        
    Rule {
        inputs: ["pngFiles", "fontFiles"]
        inputsFromDependencies: ["application"]
        Artifact {
            filePath: product.targetName + ".out"
            fileTags: ["exampleArtefact"]
        }
        ...
    }
    ...
}

В данном примере у нас есть 2 продукта и 1 модуль:

  1. Продукт с названием simpleApplication имеющий тип application (элемент Application по сути является Product {type: «application»}) содержащая 3 файла: main.cpp, app.h, app.cpp. Этот продукт зависит от модуля cpp, что указывает на то, что будет выполняться компиляция этого продукта компилятором C++ и на выходе получится артефакт помеченный тегом «application» и названием указанным в свойстве targetName, т.е. appSimple.exe (для windows или appSimple для unix платформ).

  2. Продукт с названием example имеющий тип exampleArtefact содержащий 7 файлов помеченных тремя тегами. Этот продукт зависит от продукта simpleApplication, что указывает на то, что он будет обрабатываться после создания продукта simpleApplication. А также от модуля exampleModule, что указывает на то, что для создания данного продукта будут браться правила преобразования и свойства из этого модуля. И на выходе ожидается артефакт с названием myArtefact типа (с тегом) exampleArtefact.

  3. Модуль exampleModule содержит правило преобразования файлов с тегами pngFiles и fontFiles, а также артефактов имеющих тег application, которые берутся из зависимостей собираемого продукта.

При сборке продукта будет определен список модулей от которых зависит продукт и сами модули. В них будет осуществлен поиск правил преобразования файлов помеченных входными тегами в выходные артефакты, которые соответствуют типу собираемого продукта. Сначала собирается продукт simpleApplication, т.к. он имеет зависимость только от модуля cpp. В нем ищутся правила преобразования файлов продукта в тип application. В модуле cpp есть элементы FileTagger которые по шаблону задают для файлов продукта теги. Преобразование входных файлов в выходной(ые) может быть выполнено как сразу, так и по цепочке преобразований файлов одного типа в другой, а затем в итоговый. На выходе обработки продукта simpleApplication, мы получим приложение appSimple имеющее тип (тег) application.

Затем начнется сборка продукта example. Для его файлов будет искаться правило, на выходе дающее артефакты типа exampleArtefact. Это правило требует для входа файлы типа (с тегом) pngFiles, fontFiles и application. При этом файлы типа application ищутся только в продуктах от которых зависит собираемый продукт. Так как продукт example уже содержит такие файлы, то на вход правила поступают файлы: imgA.png, imgB.png, imgC.png, fontA.ttf, fontB.ttf и appSimple.exe. А на выходе получим файл myArtefact.out типа exampleArtefact, который и будет являться нашим конечным продуктом.

Выходные артефакты


В качестве выходных артефактов для правила могут быть как один, так и несколько артефактов. Для описания выходных артефактов используется элемент Artifact:

Artifact {
    filePath: input.fileName + ".out"
    fileTags: ["txt_output"]
}

Этот элемент описывает, какой артефакт получается на выходе правила. Через свойство filePath — указывается имя выходного файла. Если указывать относительный путь, то Qbs будет создавать этот артефакт относительно каталога сборки текущего собираемого продукта. Через свойство fileTags указывается список тегов, которые будет иметь артефакт после его создания. Это необходимо, для того чтобы другие правила сборки могли использовать выходные артефакты данного правила, как свои входные артефакты. Также если продукт будет иметь этот же тег в качестве своего типа, то эти артефакты будут являться результатом сборки этого продукта.

У каждого правила должен быть хоть один выходной тег, иначе правило работать не будет. Если артефактов несколько, то можно описать несколько элементов Artifact либо можно воспользоваться свойствами outputArtifacts и outputFileTags для элемента Rule.

Свойство outputArtifacts описывает список JavaScript объектов имеющих свойства, как у элемента Artifact. Используется это свойство для случаев, когда набор выходов не фиксирован, а зависит от содержания входных данных. Например:

outputArtifacts: [{
    var artifactNames = inputs["pngFiles"].map(function(file){
        return "pictures/"+file.fileName;
    });
    artifactNames = artifactNames.concat(inputs["fontFiles"].map(function(file){
        return "fonts/"+file.fileName;
    }));
    artifactNames = artifactNames.concat(inputs["application"].map(function(file){
        return "app/"+file.fileName;
    }));
    var artifacts = artifactNames.map(function(art){
        var a = {
            filePath: art,
            fileTags: ["exampleArtefact"]
        }
        return a;
    });
    return artifacts;
}]

В данном примере показано, что для входных файлов с тегом pngFiles, правило подготовит выходной артефакт с таким же названием и поместит его в папку pictures. Для тегов fontFiles и application, также поместив их соответственно в папки fonts и app. При этом т.к. пути будут относительные, то эти папки создадутся в папке сборки продукта.

Если мы решили использовать свойство outputArtifacts, то необходимо указать и свойство outputFileTags, которое является списком выходных тегов, которые правило потенциально производит. Для нашего примера:

outputFileTags:
 ["exampleArtefact"]

Все полученные при сборке продукта артефакты, помеченные выходным тегом совпадающим с типом продукта, при установке продукта, будут скопированы из каталога сборки в каталог установки.

Правило преобразования


Когда определены входные и выходные артефакты, необходимо подготовить саму последовательность команд для выполнения преобразования. Для этого используется свойство prepare элемента Rule. Это свойство является JavaScript сценарием, который возвращает список команд для преобразования входов в выходы. Код в этом скрипте рассматривается как функция с сигнатурой function(project, product, inputs, outputs, input, output).
Параметры input и output не определены (undefined), если для этого правила имеется несколько артефактов ввода (и вывода соответственно). Служат они как синтаксический сахар: input = inputs[0] и output = outputs[0] и являются списками с одним элементом. Параметры project и product, являются JavaScript объектами, через которые доступны свойства текущего проекта и продукта соответственно. Особый интерес вызывают объекты inputs и outputs. Рассмотрим их более подробно.

Объекты inputs и outputs


Параметры inputs и outputs являются объектами JavaScript, ключи свойств которых являются файловыми тегами, а значениями свойств — являются списки объектов, представляющих артефакты, соответствующие этим тегам. В нашем примере переменная inputs имеет 3 ключа: pngFiles, fontFiles, application. А каждый входной артефакт доступен через inputs[«pngFiles»] (или inputs.pngFiles что равнозначно). Каждый артефакт в этом списке имеет следующие свойства:

  • baseName — базовое имя файла (например для файла c:\123\test.plugin.dll это будет test)
  • completeBaseName — название файла без расширения (например для файла c:\123\test.plugin.dll это будет test.plugin)
  • fileName — название файла (например для файла c:\123\test.plugin.dll это будет test.plugin.dll)
  • filePath — полный путь до файла с его полным именем
  • fileTags — список тегов присвоенных артефакту

Помимо этого артефакты содержат в себе все свойства для каждого модуля, который используется в продукте. Эта особенность может использоваться для доступа к свойствам модуля. Например, свойство inputs.application[0].cpp.defines вернет для артефакта simpleApplication список определений, который будет передан при компиляции соответствующего файла. Это очень удобный и важный момент, позволяющий артефактам задать через свойства какого-нибудь модуля свои значения и группировать такие артефакты или обрабатывать их как-то по особенному.

* Было подмечено на Qbs версии 1.7.2, что если продукт подменяет свойства модуля в котором находится правило сборки, то эти свойства недоступны в артефакте. По этому эти свойства я выносил в отдельный модуль.

* Также inputs.application[0].cpp.defines не всегда срабатывает, по этому я использую функцию inputs.application[0].moduleProperty(«cpp», «defines»). Если эту функцию применять к входному артефакту, то будут возвращаться свойства которые использует артефакт в указанном модуле. Если же применять ее к продукту (например product.moduleProperty(«cpp», «defines»), то возвращаться будут свойства указанного модуля, которые использует собираемый в данный момент конечный продукт.

* Очень удобной функцией является dumpObject(object) из модуля ModUtils, которая выводит в консоль информацию о свойствах переданного в него параметра. Правда и она не всегда показывает свойства используемых в артефактах модулей.

Команды


В качестве результата выполнения скрипта выступает список команд. Команда — это то, что Qbs выполняет во время сборки. Команда всегда создается в сценарии подготовки правила. Команды бывают двух типов:

  1. Command, который запускает внешний процесс
  2. JavaScriptCommand, который выполняет произвольный код JavaScript (хотя может также запускать внешние процессы)

Для обоих типов команд доступны свойства:

  • description — строка, показываемая в консоли при выполнении данной команды
  • extendedDescription — строка детальной информации, показываемой в консоли при расширенном выводе
  • highlight — тип (тег) команды. Влияет на то, как будет показываться description в консоли. Может быть: compiler, linker, codegen, filegen и пр.
  • silent — скрывать ли вывод description при выполнении команды

Для использования Command, необходимо создать объект передав в его конструктор полный путь до исполняемой программы и список аргументов.

var myCommand = new Command("dir", ["/B", "/O", ">>", output.filePath]);

Например данная команда запишет вывод команды dir в выходной файл правила сборки.
Полезными свойствами, позволяющими обойти ограничение Windows на длину командной строки являются свойства:

  • responseFileThreshold содержит значение. Если это значение больше нуля и меньше длины полной командной строки, и если responseFileUsagePrefix не пуст, то содержимое командной строки перемещается во временный файл, путь которого становится полным содержимым списка аргументов. Затем программа должна прочитать полный список аргументов из этого файла. Этот механизм будет работать только с программами, которые явно поддерживают его.
  • responseFileArgumentIndex — указывает индекс первого аргумента для включения в файл ответов.
  • responseFileUsagePrefix — содержит префикс, который сообщает программе, что остальная часть аргумента — это путь к файлу, содержащему фактическую командную строку.

Для обработки вывода выполняемой команды могут применяться свойства:

  • stderrFilterFunction — функция с входным параметром равным выводу программы в stderr и которая должна вернуть преобразованную строку для дальнейшего вывода.
    Если не задана, то обработки не будет производиться.
  • stdoutFilterFunction — аналогичная stderrFilePath, только работающая с stdout
  • stdoutFilePath — название файла, кода направляется отфильтрованный ранее вывод stdout. Если не задан, то вывод будет производиться в консоль
  • stderrFilePath — аналогичная stdoutFilePath, только работает с stderr

JavaScriptCommand команда — представляет собой JavaScript функцию, которая будет выполняться при сборке. Задается это функция в свойстве sourceCode. В исходном коде функции доступны project, product, inputs и outputs (дающий доступ к свойствам проекта, продукта, входных и выходных артефактов соответственно). Чтобы передать в функцию произвольные данные, то для команды надо добавить произвольные свойства и присвоить им нужные значения. Например так:

var cmd = new JavaScriptCommand();
cmd.myFirstData = "This is 1 string";
cmd.mySecondData = "This is 2 string";
cmd.sourceCode = function() {
    console.info("String from source code");    // -->> "String from source code"
    console.info("Property 1: "+myFirstData);   // -->> "Property 1: This is 1 string"
    console.info("Property 2: "+mySecondData);  // -->> "Property 2: This is 2 string"
};

В функции, также можно использовать разные доступные сервисы:

  • Environment Service — для доступа к системной среде и среде разработки
  • File Service — для работы с файлами (копирование, перемещение, удаление файлов, проверки существования файла и даты модификации, создания путей и сканирования директории на содержимое)
  • FileInfo Service — операции обработки пути к файлу (получение базового имени файла, пути к нему, получение относительного пути и т.п.)
  • General Services — дополнительные функции расширяющие функционал обработки строк, массивов и т.п.
  • Process Service — функции позволяющие запускать и управлять внешними программами, а также работать с его входом и выходом
  • PropertyList Service — функции для работы со списками свойств в форматах JSON, XML, binaty и OpenStep для платформ Darwin (iOS, macOS, tvOS, и watchOS)
  • TemporaryDir Service — функции для создания и управления временным каталогом
  • TextFile Service — функции для работы с текстовыми файлами и позволяющие читать/писать из/в них.
  • Utilities Service — функции для получения хешей

Прочие свойства элемента Rule


У элемента Rule также есть несколько дополнительных свойств:

Свойство multiplex, необходимо для указания порядка обработки входных артефактов. При multiplex=true, создается одна копия правила для всех входных артефактов и они обрабатываются все скопом. Таким образом в свойстве inputs будут все входные артефакты. Применяется в случаях, когда надо производить групповую обработку входных артефактов. Если же multiplex=false, то для каждого входного артефакта будет создаваться отдельная копия правила преобразования и будет создаваться свой выходной артефакт. Таким образом свойство inputs будет содержать всегда один элемент. По умолчанию это свойство имеет значение false.

Свойство condition, указывает на условие выполнения правила. Например, указать, что преобразование будет выполняться только в режиме release и для платформы windows.

 qbs.buildVariant === "release" && qbs.targetOS.contains("windows") 

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

Свойство alwaysRun указывает, на условия выполнения команд в правиле. Если alwaysRun=true, то команды будут всегда выполняться, даже не взирая на то, что выходные артефакты уже обновлены. По умолчанию равно false.

Пример подготовки правила преобразования



В качестве примера правила приведу следующую задачу:

Имеется проект, из нескольких программ и библиотек, для которого необходимо скопировать все необходимые для работы программ Qt библиотеки и плагины в заданную директорию. Для этого создаем модуль с правилом преобразования и самостоятельный продукт, который будет зависеть от всех продуктов проекта, для которых и необходимо подготовить Qt библиотеки. Дополнительно надо подготовить тестовый файл с указанием всех подготовленных Qt библиотек.

winDeployQt - деплой Qt библиотек для проекта
-- MyWindowsDeploy.qbs --
import qbs
import qbs.ModUtils

Module {
Rule {
        condition: qbs.targetOS.contains("windows") //запускается только для windows
        multiplex: true //обрабатываем все входные артефакты одним трансформером 
        alwaysRun: true //всегда выполнять правило

        inputsFromDependencies: ["installable"] //брать устанавливаемые артефакты от зависимостей собираемого продукта
        Artifact {
            filePath: "Copied_qt_libs.txt";
            fileTags: ["deployQt"];
        }

        prepare: {
            var cmdQt = new JavaScriptCommand();
            //определяем путь до windeployqt.exe
            cmdQt.windeployqt = FileInfo.joinPaths(product.moduleProperty("Qt.core", "binPath"), "windeployqt.exe");
            //задаем строку для вывода в консоли
            cmdQt.description = "Copy Qt libs and generate text file: "+output.fileName;
            //указываем развернутую информацию по выполняемой команде
            cmdQt.extendedDescription = cmdQt.windeployqt + ".exe " +
                    ["--json"].concat(args).concat(binaryFilePaths).join(" ");
            
            //путь до папки, куда надо копировать qt библиотеки ("<папка установки>/QtLibs")
            var deployDir = FileInfo.joinPaths(product.moduleProperty("qbs","installRoot"),
                                            product.moduleProperty("qbs","installDir"));
            deployDir = FileInfo.joinPaths(deployDir, "QtLibs");
            cmdQt.qtLibsPath = deployDir;
            
            //определяем аргументы запуска программы
            cmdQt.args = [];
            cmdQt.args.push("--libdir", deployDir);
            cmdQt.args.push("--plugindir", deployDir);
            cmdQt.args.push("--no-translations");
            cmdQt.args.push("--release");
            
            //полное имя файла для записи вывода.
            cmdQt.outputFilePath = output.filePath;
            
            //определяем список путей установки программ и библиотек, от которых зависит продукт
            cmdQt.binaryFilePaths = inputs.installable.filter(function (artifact) {
                return artifact.fileTags.contains("application")
                        || artifact.fileTags.contains("dynamiclibrary");
            }).map(function(a) { return ModUtils.artifactInstalledFilePath(a); });
          
            cmdQt.sourceCode = function(){
                var process;
                var tf;
                try {
                    //выводим значения параметров
                    console.info("windeployqtRule: outputFilePath: "+outputFilePath);
                    console.info("windeployqtRule: qtLibsPath: "+qtLibsPath);
                    console.info("windeployqtRule: windeployqt: "+windeployqt);
                    console.info("windeployqtRule: windeployqtArgs: "+windeployqtArgs.join(", "));
                    console.info("windeployqtRule: binaryFilePaths: "+binaryFilePaths.join(", "));
            
                    //создаем папку куда будут скопированы библиотеки Qt
                    File.makePath(qtLibsPath);
                    //создаем процесс
                    process = new Process();
                    //запускаем процесс
                    process.exec(windeployqt,
                                 ["--json"].concat(windeployqtArgs).concat(binaryFilePaths), true);
                    //читаем вывод программы
                    var out = process.readStdOut();  
                    //парсим выходной JSON
                    var inputFilePaths = JSON.parse(out)["files"].map(function (obj) {
                        //определяем полный путь доя скопированной библиотеки
                        var fn = FileInfo.joinPaths(
                                    FileInfo.fromWindowsSeparators(obj.target),
                                    FileInfo.fileName(
                                        FileInfo.fromWindowsSeparators(
                                            obj.source)));
                        return fn;
                    });
                    //создаем файл
                    tf = new TextFile(outputFilePath, TextFile.WriteOnly);
                    //пишем заголовок
                    tf.writeLine("Copied Qt files:");
                    inputFilePaths.forEach(function(qtLib){
                        tf.writeLine(qtLib); //записываем в выходной файл полный путь до скопированной библиотеки
                    });
                } finally {
                    if (process)
                        process.close();
                    if (tf)
                        tf.close();
                }
            }

            return [cmdQt];
        }
    }
}


Пример продукта
-- ProductDeploy.qbs --
import qbs

Product {
//продукт представляет собой результат деплоя Qt библиотек для продуктов от которых он 
//зависит в виде скопированных библиотек и текстового файла
    type: ["deployQt"] 

   // указываем зависимость от модуля в котором есть правило преобразования
    Depends { name: "MyWindowsDeploy" } 
   // указываем зависимость от продуктов, для которых 
   // надо подготовить Qt библиотеки, плагины и пр.
    Depends { name: "libratyA" }
    Depends { name: "libratyB" }
    Depends { name: "applicationA" }
    Depends { name: "applicationB" }
    Depends { name: "applicationC" }

    condition: qbs.targetOS.contains("windows")     // собирать только для windows
    builtByDefault: false // собирать ли продукт при общей сборке проекта

    qbs.install: true
    qbs.installDir: "MyProject/qtDeploy"
}


Ссылки


Поделиться с друзьями
-->

Комментарии (52)


  1. mapron
    06.06.2017 18:01
    +1

    Забавно) Я тоже когда-то пробовал создавать инсталляторы с помощью qbs; тогда еще 1.3 версия у меня была. Даже относительно неплохо выглядело. Но в итоге сейчас я qbs не использую, увы, он меня не устраивает.


    1. ukhegg
      06.06.2017 21:15
      +1

      А чем не устраивает? и чем пользуетесь? Сам пользуюсь cmake-ом, интересно, стоит ли вообще qbs изучать


      1. hooligan
        07.06.2017 07:52

        Объективно могу сравнить только с qmake: производительность сборки увеличилась значительно. Например полная пересборка проекта qmake занимала около 40-50 минут, qbs же пересобирает в среднем за 15, т.к. грузит все ядра процессора. В дальнейшем он собирает только новые и изменившиеся файлы, что делает быстро. Явным плюсом к этому еще то, что он очень хорошо отслеживает зависимости и например при изменении чего-либо в статической библиотеки, собрать заново и использующие ее другие продукты, чего qmake не делает и об этом надо всегда помнить.
        Qbs конечно хорош, но все конечно дело привычки. Читать его удобно и понятно, а вот писать задуманное — не сразу удается, т.к. мало информации. В любом случае думаю попробовать на каком-нибудь небольшом проекте его стоит.


        1. ser-mk
          07.06.2017 16:40

          qmake сильно и не нужно грузить процессор. Он же только создает Makefile с которым дальше работает make и там уже можно разными опциями задавать кол-во процессов.


          1. hooligan
            07.06.2017 17:13

            Да, вы правы — не правильно выразился. Qmake только генерирует Makefile, при этом ему можно указать в качестве параметра -j<кол-во потоков для дальнейшей сборки>. Но проблема заключается в том, что это действует только на основной Makefile, в реальности же для каждого профиля сборки далее создается свой Makefile, который и будет в дальнейшем обрабатываться make (Makefile.Debug или Makefile.Release), а на него этот аргумент уже не будет распространяться и все будет выполняться в одном потоке. Немного исправляет ситуацию утилита jom, но все равно медленнее чем qbs выходит


            1. mapron
              07.06.2017 17:35

              jom оставил у меня двоякое впечатление. С одной стороны, он гораздо лучше make грузит несколько потоков, с другой стороны при распределенной сборке — когда у вас 100 потоков компиляции допустим, сильно проигрывает ninja + часто зависает без видимой причины. (часто это в 1 из 30 запусков допустим — для CI что-то дофига).


        1. mapron
          07.06.2017 17:34

          По моим личным замерам, ninja работает с графом сборки куда эффективнее нежели qbs. это даже Jake Petroules не отрицал в комментах к релизу; только он почему-то упорно сравнивал инкрементный билд qbs с cmake+ninja, а не просто ninja, хотя я в упор не могу понять нафига при инкрементной сборке переконфигурирование.


        1. DaylightIsBurning
          07.06.2017 18:36

          qmake занимала около 40-50 минут, qbs же пересобирает в среднем за 15, т.к. грузит все ядра процессора

          А как так получается? У меня «qmake» (на самом деле просто «make») тоже исспользует все ядра:
          cd build
          qmake ..
          make -j12
          


          1. hooligan
            08.06.2017 10:55

            Дело было года 2-3 назад и сборка производилась из-под QtCreator (версию уже не помню — 2.хх скорее всего). Вероятно он передавал некорректно этот атрибут для make. Сейчас поднял проект со сборкой на qmake и запустил пересборку его из-под QtCreator 4.3.0 — mingw32-make действительно распараллелил компиляцию и загрузил все ядра. Для интереса провел тест и получил интересные результаты: mingw32-make собрал за 10:19, jom за 09:27 и qbs за 11:28. Правда сопоставить сборку qbs с другими способами не совсем корректно будет, т.к. проект переехал на qbs и в нем есть изменения которых нет в qmake. Но разницу между чистым qmake и jom все таки можно увидеть.


      1. mapron
        07.06.2017 17:37
        +2

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


    1. hooligan
      07.06.2017 08:15

      Какую систему сборки используете сейчас, если не секрет?


      1. mapron
        07.06.2017 17:33

        Не секрет, cmake; Используемые генераторы: MSVS, Xcode, Ninja + CodeBlock для QtC, Makefiles для CLion, Ninja на билд-серверах.
        Там где Ninja- используем свою систему распределенной сборки, я ее уже чуток пиарил на хабре: https://github.com/mapron/Wuild/. Ну и для makefiles/xcode она тоже (хоть и немного менее эффективно) годится.

        qbs не устраивает по двум параметрам: слабая интеграция с произвольной IDE (точнее — никакая, вмерджен только начатый мной же патч для VS — и это спустя 2 года!), отсутствие интеграции с системами распределённой сборки; отсуствие коммьюнити/внятной поддержки в mailing list. По cmake туева куча готовых решений.

        Синтаксис? Да, qbs им прекрасен. Кода на нем писать придется меньше и он будет читаемее. Но увы, этого маловато(


  1. aknew
    06.06.2017 23:49
    +1

    Может не в тему, но все-таки спрошу — если qbs так хорош и создан чтобы заменить qmake, почему же он до сих пор так и не стал основной системой сборки для qt? Ведь с его представления прошло уже больше 5 лет, у него есть какие-то скрытые проблемы?


    1. hooligan
      07.06.2017 08:14

      Начиная с Qt Creator 2.7 он входит в комплект поставки (например https://habrahabr.ru/post/171405/ и https://habrahabr.ru/post/181688/). Правда его интеграция в IDE до сих пор пока не полная (имею ввиду подсказки, дополнения и т.п.). Насколько знаю (пруфов не могу найти), сам Qt Creator с его инфраструктурой собирается с помощью qbs и поставляется в их же инсталляторе qt Install Framework. Если вы под

      не стал основной системой сборки для qt
      имеете ввиду что он не предлагается в creator как система сборки проектов по умолчанию и доступна только при включении плагина, то этот вопрос думаю следует адресовать разработчикам этого продукта.
      Qbs — определенно лучше qmake, но он не герболайф — у него тоже есть некоторые недостатки и иногда возникают неясные моменты в его работе. Например, иногда он не может на ровном месте создать каталог сборки продукта собираемого по моим правилам — или конечно я чего-то не указываю. Все сводится к тому,
      что много чего не документировано, а если и документировано, то местами лучше бы этого не делали ))
      При nokia такого не было. Понабрали хипстеров и забили на документацию


      1. aknew
        07.06.2017 10:03

        Спасибо вам и остальным ответившим. То что он не герболайф понятно, в их существование, наверное, верят только джуниоры. Просто то что его когда-то с такой помпой объявили и 5 лет он подвизается где-то в виде отдельного плагина или же системы сборки в компании с совсем сторонним cmake не может не смущать.
        А неясных моментов и багов на ровном месте (типа «не тот z поставили» или «забыли что наша библиотека имеет несколько способов создания объектов») в qt что-то в последнее время стало сильно больше, видимо вы правы про нокию (а может я старею и стал занудой, потому как субъективно кажется что это не только с qt такая проблема)


        1. mapron
          07.06.2017 17:45

          Qbs определенно лучше qmake — да, это бесспорно. Но вот если бы Qt Project на его разработку выделяло не 1.5 разработчика (или даже меньше), а чуть побольше, и за несколько лет уделило время на решение типовых пользовательских кейсов — те же генераторы под частые форматы (либо взамен их интеграцию с 2-3 популярными IDE) — это бы привлекло возможно куда больше контрибьюторов (и багов было бы меньше).

          А так — я сидел плевался с 64-битным mingw с вылезающими багами в 1.6,1.7, 1.8 — конечно, прикольно что можно все в js файлах поправить, не пересобирая, но все же…


          1. RPG18
            08.06.2017 09:44

            Но вот если бы Qt Project на его разработку выделяло не 1.5 разработчика (или даже меньше), а чуть побольше

            Open Source же. Кто хочет, тот может присоединиться к разработке. Малое количество разработчиков говорит о малом интересе.


            1. DaylightIsBurning
              08.06.2017 13:51

              Малое количество разработчиков говорит о малом интересе.
              Я думаю, небольшой интерес вызван тем, что сами разработчики не знают, чего хотят от qbs и будут ли они его развивать в дальнейшем вообще. Это по состоянию на сентябрь 2016, соответсвенно и сообщество не спешит инвестировать в проект с неясным будущим.


              1. mapron
                08.06.2017 17:54

                Вот именно, одно дело опенсорс и побочный проект, другое дело говорить «мы на него закладываемся к следующему мажорному релизу». У меня это в голове не стыкуется никак.
                QtC тоже начинался как проект энтузиастов (примерно как мега-демка того что умеет Qt), но потом в него стали реальные ресурсы вкладывать => получился через несколько лет приличный результат. И да, я выводы свои делаю не только на основе релизов, но так же на основе рассылки, обсуждений в тикетах, комментах в пуллреквестах, я так понимаю, многие QtC разработчики скептично к нему относятся (мол, надо реальные штуки для пользователей пилить, а вы какие-то оптимизации билдграфа делаете и мутите dynamic-variadic-Rules которые хз кому нужны) — сильно перевираю цитату, Oswald в комментах какомуто задеклайненному реквесту писал, в начале 2016 года (тогда еще не влили msvs генератор)


    1. ailyou
      07.06.2017 08:26

      С релизом QBS 1.8 разработчики объявили, что станет основным в Qt 6.


      1. mapron
        07.06.2017 17:46

        Смелое заявление. Либо Qt 6 планируют выпустить лет через 10, либо все же в qbs придет команда сильных разработчиков которые будут ему уделять время фулл-тайм. Иначе не вижу способа сдержать обещанное.


    1. Antervis
      07.06.2017 09:28

      Моё мнение как человека, несколько лет работающего с qbs: в большинстве случаев он очень хорош. Синтаксис прекрасен. Поддерживает не только c++ проекты. Существуют недостатки (о них ниже), которые может быть не очень-то и просто обойти. Не хватет документации и user experience — на почти все вопросы, которые я задавал на SO по qbs, отвечал один человек.

      По умолчанию (и это не изменить) qbs собирает всё в загогулистую подпапку папки билда. Лучше всегда указывать destinationDirectory. Зато если destinationDirectory указан, временные файлы этапа компиляции не перемешиваются с таргетами.
      Он пока еще не в полной мере поддерживает функционал qmake (пример: TYPELIBS).
      Часть функционала не документирована или документирована недостаточно (пример: qbs.installSourceBase)
      Лично у меня введение дополнительных этапов сборки зачастую вызывали затруднения (скажем, «выполнить somescript в процессе сборки, после компоновки»). Добавлять свои скрипты к install step нельзя
      Списковые свойства (типа cpp.includePaths,cpp. dynamicLibraries и пр.) в разных контекстах могут не доопределяться, а переопределяться (доопределение в случае изменения Export или Depends на модуль; переопределение при «наследовании»).
      qbs накладывает ограничения на иерархию файлов проекта. (пример: qbs-ники модулей обязаны лежать в qbsSearchPaths+"/modules/MyModuleName/MyModuleName.qbs"). Сабмодули не документированы.
      Ругается на «некошерное» использование некоторого API (пример: File.DirectoryEntries)
      При запуске из-под QtCreator не поддерживает console.log. Дебажить приходится через throw.

      Это список тех подводных камней, с которыми я часто встречаюсь. В моём понимании, процесс билда завершен только когда приложение можно отдавать пользователю. qmake файлы трудно читать, но он пока еще более гибкий.


      1. mapron
        07.06.2017 17:47

        install step-ы имхо очень неочевидные у них. Я так и не понял как вообще там можно сделать в рамках одного проекта фиксап саббандлов для макоси… чтобы с подписью и все такое)


      1. mapron
        07.06.2017 17:47

        «Дебажить приходится через throw.» — не следил за последними релизами, ДО СИХ ПОР?? это же ахтунг)


  1. DaylightIsBurning
    07.06.2017 19:09

    Зачем инвестировать в изучение qbs если есть cmake, который может всё тоже и намного больше, и уже является стандартом де-факто, а qbs на это даже не претендует?

    Я всё таки не понимаю, почему в Qt решили создавать qbs, а не перейти на cmake? Я просматривал пост разработчиков, где они обсуждают cmake, но кроме «нет того, чего нам хотелось бы» никаких причин не увидел. Так и хочется ответить: «Ну так пользуйтесь тем, что есть»! Мне как разработчику очень не хочется изучать ещё одну систему сборки, которую я смогу использовать только для qt-проектов и то не понятно когда, т.к. qbs до сих пор не стал основной системой сборки даже для Qt. Я бы понял, если бы они решили сделать киллера cmake, не завязанного на Qt, а претендующего на замену cmake во всех его основных нишах, но для этого нужно выделить кучу ресурсов, чего не видно.


    1. DaylightIsBurning
      07.06.2017 20:15

      Видимо, разработчики Qt и сами осознают проблему и по состоянию на сентябрь 2016 сами не решили, перейти ли на cmake или продолжать развивать qbs и, если да, то в каком виде: qt-specific tool или универсальной build-tool.

      We are thinking about switching build systems. We don't know what to do yet…


    1. Antervis
      07.06.2017 21:50

      Мне как разработчику очень не хочется изучать ещё одну систему сборки, которую я смогу использовать только для qt-проектов

      так он же не только для qt-проектов


      1. DaylightIsBurning
        08.06.2017 14:03

        теоретически. На практике никаких подвижек в сторону того что бы сделать qbs действительно универсальным build tool не видно.
        К примеру, я не смог найти механизма поиска внешних зависимостей аналогичного find_package() в cmake. Как собирать приложение с boost и python bindings, к примеру, в ручную для каждой платформы всё прописывать? И это я ещё не отошел от c++.


        1. hooligan
          08.06.2017 16:22

          В основном у них используется для этих целей Probe элемент. Но опять же для каждой платформы надо будет реализовывать его снова и точно знать как искать зависимость. Что порой не очень удобно и универсально…


    1. RPG18
      08.06.2017 09:55

      Я всё таки не понимаю, почему в Qt решили создавать qbs, а не перейти на cmake?

      Как бы CMake Manual в официальной документации, да и cmake модули поставляются с Qt.


      1. DaylightIsBurning
        08.06.2017 14:10

        Они поддерживают cmake, но не так хорошо как qmake, и решения об использовании cmake как основного build tool в будущем принято не было, хотя, видимо, такая возможность до сих пор рассматривается.


        1. RPG18
          08.06.2017 14:13

          Они это кто? Digia, KDE Free Qt Foundation или кто-то еще?


          1. DaylightIsBurning
            08.06.2017 14:22

            разработчики qbs и Qt в целом, судя по QtCon 2016.


          1. DaylightIsBurning
            08.06.2017 14:27

            В частности, QtCreator работает с cmake не так хорошо, как c qmake.

            (Tobias) Cmake is the «worst» system in Qt Creator because CMake doesn't give enough feedback to the IDE. We need first-class support for CMake in Qt Creator...

            Мне кажется, сделать убийцу cmake было бы здорово, но для этого нужно много ресурсов и, как минимум, нужно поставить такую цель. А делать buid-system с мыслью что она будет использоваться только в контексте Qt — странная затея, на мой взгляд.


            1. RPG18
              08.06.2017 16:35

              Как бы не стоит мешать Qt и QtCreator.


              Мне кажется, сделать убийцу cmake было бы здорово, но для этого нужно много ресурсов и, как минимум, нужно поставить такую цель

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


              1. mapron
                08.06.2017 17:56

                К сожалению, огромному, из коммерческих компаний — не нужно никому. Как я понял, по косвенным комментам, даже разработчикам Qt Project на qbs время не выделяют особо. Может я и ошибаюсь, но не вижу ничего сильно доказывающего обратное.


                1. RPG18
                  08.06.2017 18:08

                  Как бы есть еще KDE Free Qt Foundation, только и KDE использует CMake.


                  1. mapron
                    08.06.2017 18:32

                    Ну, использовать одно. А именно активно контрибьютить, причем желательно чтобы разработчик этому уделял полное время на зарплате — это другое.
                    Так вон и 2гис qbs использует вовсю — толку-то?


                    1. RPG18
                      09.06.2017 12:19

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


                1. DaylightIsBurning
                  09.06.2017 13:15

                  К сожалению, огромному, из коммерческих компаний — не нужно никому

                  Google нужно!


                  1. mapron
                    09.06.2017 19:14

                    я не про любых убийц cmake, я конкретно про qbs =)


              1. DaylightIsBurning
                08.06.2017 18:11

                Разработчикам это было бы полезно как инфраструктурная вещь — cmake не слишком удобен и вполне понятно, что можно было бы сделать лучше. Кто из организаций хочет этим заняться я не знаю. Выглядит так, будто никто вообще не знает, в какую сторону будет развиваться qbs и не умрёт ли через год.

                Как я понял, изначально qbs родился как замена qmake, который стало слишком сложно поддерживать, о чем Trolltech писали ещё в 2009 (или раньше). Qmake, на сколько я могу судить, никогда не претендовал на то, что бы стать универсальной билд-системой типа cmake, а чисто занимал нишу билд-тула для Qt-проектов. Соответсвенно, поскольку qbs начался как наследник qmake — можно подумать, что его нацеливали в ту же нишу. Однако в этой нише он, имхо, не нужен т.к. сегодня у нас есть cmake. Но и чёткой цели создать конкурента cmake перед qbs тоже, похоже, не поставили.


                1. RPG18
                  08.06.2017 18:18

                  cmake не слишком удобен

                  И что же это KDE Free Qt Foundation, которая отвечает за развитие open source версии Qt, не взялось за это, а использует "неудобный" CMake?


                  1. DaylightIsBurning
                    08.06.2017 18:45

                    Потому что не считают, что создание более удобного build-tool — это для них приоритетная задача, или, может быть считают, что такая задача им не по силам? Это ведь огромная работа — нужно не только синтаксис удобный продумать, но и как-то обеспечить поддержку нескольких в IDE, научить систему поддержке разных зависимостей хотя бы на основных 3х-5ти платформах и т.д.

                    А чисто красивого синтаксиса и скорости не достаточно, есть ведь tup, но что-то я не вижу что бы он заменил cmake.


                    1. RPG18
                      09.06.2017 12:17

                      Потому что не считают, что создание более удобного build-tool

                      Это все очень субъективно.


                      но и как-то обеспечить поддержку нескольких в IDE

                      Это проблема конкретных IDE поддержать инструменты иначе это уже не Integrated Development Environment


                      1. DaylightIsBurning
                        09.06.2017 12:43
                        +1

                        Это проблема конкретных IDE поддержать инструменты иначе это уже не Integrated Development Environment
                        Ну почему же, Integrated не означает Ultimately Universal. Integrated значит что то, что поддерживается — хорошо интегрировано с другими поддерживаемыми инструментами. В каких IDE есть нормальная поддержка qbs? Кроме того, я не уверен, что разработчик предпочтёт отказаться от любимой IDE в пользу любимого build-tool. Что бы IDE стали поддерживать build-tool, tool должен быть достаточно важным, а что бы этого добиться нужно приложить немало усилий, и создание плагинов для популярных IDE скорее всего положительно отразится на развитии самого тула, и увеличит вероятность того, что и другие IDE тоже станут его поддерживать. Поэтому мне кажется, что разработчики нового билд-тула более заинтересованы в разработке плагинов под популярные IDE, чем разработчики этих IDE.


                        1. mapron
                          09.06.2017 19:15

                          Я с вами полностью согласен. С тем же cmake — это его разработчики создают и оттачивают генераторы, а не разработчики MSVS, Xcode, kate или CodeBlocks =)


                          1. RPG18
                            09.06.2017 20:45

                            Что-то разработчики make не оттачивают плагины к MSVS, Xcode, CodeBlocks, CLion, Eclipse.


                            1. mapron
                              10.06.2017 07:03

                              make такая же система сборки, как и msbuild или xcodebuild. только плюс к тому еще и древняя, когда она начинала получать распространение — об интеграции IDE думать не приходилось.
                              разработчик ninja тоже как-то не задумывается о плагинах =)

                              если хотите проводить такую же аналогию то лучше подойдут autotools. Ну и да, там разработчики сильно не заморачивались, и посмотрите сколько использует autotools и сколько — cmake. Я для сборки ffmpeg написал cmake-скрипты только чтобы была возможность отладки в IDE удобным путем =)

                              Вообще, на самом деле еще все очень сильно зависит от устоявшейся ситуации на рынке. Допустим, если я сейчас напишу свою IDE для C++, то будет очень странно, если там не будет интеграции с cmake и возможности сборки через make или ninja (лучше если оба). Если этого нет — то сразу количество моих пользователей резко падает. Соответственно, разработчики cmake уже будут незаинтересованы.
                              У IDE XCode и VS — большая аудитория. Много разработчиков их использует и к ним привыкли. Если я делаю свою супер-пупер крутую систему сборки, которая рвет конкурентов и все такое — но забываю про удобную интеграцию с популярными IDE — я теряю своих пользователей. У меня нет пользователей — разработчики IDE не хотят заморачиваться с поддержкой меня) замкнутый круг.
                              Посмотрите на решения от Xoreax — они предлагают интеграцию с целым рядом сборочных систем (про качество и цену я сейчас умолчу — но сам подход я считаю верным).

                              В итоге — либо ты обладаешь большой аудиторией и можешь другим навязывать свою стратегию, либо подстраиваешься под других, если хочешь выползти наверх. Хорошо, плохо, аморально ли это — это за пределами моего утверждения.
                              Да, в «топе» могут держаться далеко не идеальные системы сборки и IDE =) но зато это дает больше шансов «ворваться» кому-то еще.


                              1. RPG18
                                10.06.2017 15:31
                                -1

                                об интеграции IDE думать не приходилось.

                                Всем фиолетово.


                                Допустим, если я сейчас напишу свою IDE для C++

                                А может лучше вы поможете в разработке qbs, вместо того, что бы жаловаться про 1.5 разработчика.


                                Соответственно, разработчики cmake уже будут незаинтересованы.

                                Откуда интерес, если они не имеют с этого денег?


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

                                Какой верх у open source решений? Сторонние разработчики добавляют в cmake то, что им нужно, а не ждут пока kitware добавит генератор под IDE.


                                1. mapron
                                  10.06.2017 17:13
                                  +1

                                  А может лучше вы поможете в разработке qbs, вместо того, что бы жаловаться про 1.5 разработчика.

                                  https://bugreports.qt.io/browse/QBS-31

                                  Кхм. Я сделал пуллреквест. Исправил замечания. Можете посмотреть. Потом спросил «чуваки, что еще надо, чтобы он попал в апстрим?» почитайте комменты на геррите.
                                  В итоге спустя 2 года msvs генератор после еще одного рефакторинга (Jake его попилил на большее количество файлов, респект и уважуха, но не принципиально) он таки попал в апстрим.
                                  Может быть для вас это нормальный темп разработки. Но для меня он не сравним с темпом разработки самого Qt, извините. Лично для меня пыл контрибьютить в qbs угас.


                                1. mapron
                                  10.06.2017 17:18

                                  Я активно агитировал за переход на qbs, весь деплой и сборка инсталляторов одной коммерческой компании перевел на него; более того, он даже сейчас, спустя 2 года после моего ухода, остается там :) моя претензия в этой ветке выражалась в одной фразе — если Qt Project утверждает в официальном блоге что они хотят сделать qbs основной системой сборки — это ПОКА не согласуется с теми ресурсами, которые на неё выделяют. Она замечательная, няшная, и я всецело буду рад, если они действительно сделают её таковой)
                                  А вы начинаете переходить на личности «чего жалуетесь что там мало народу, сами помогайте» — ну простите, как то странно, что сами разработчики должны брать меня в расчет, когда планируют разработку :D