Package: qdep
qdepA very basic yet simple to use dependency management tool for qmake based projects.Table of contentsqdepFeaturesDesign Goals/ScopeInstallationPreparing qmakeShell completitionsGetting startedGetting deeperDependency IDsVersioningPackage-uniqueness and version conflictsNormal dependenciesTranslationsLibrary supportCreating normal dependenciesCreating qdep translationsResources and hooksAutomatic exportsProject dependenciesCreating project dependenciesDocumentationCommand line interfacePublic API operations:Private API operations:QMAKE-FeatureVariablesCommon VariablesSUBDIRS extensionAdvanced VariablesConfiguration valuesInput valueOutput valuesDefinesEnvironment variablesPublic make targetsInternalVariablesConfiguration valuesQMAKE test functionsQMAKE replace functionsTable of contents generated with markdown-tocFeaturesSeamless integration in qmake projects - no extra files neededBasic dependency management using git repositories as package sourcesGlobally caches source files to speed up buildsPackages are simple pri-files that are included by the target projectRecursive dependency solvingAllows branch and tag based versioningSupports translations for qdep packagesSupports automatic export of qdep packages from dynamic librariesHandles QRC-Resources and startup hooks to work even when used in static librariesSupports special "Project dependencies" wich allows you to add whole qmake projects to a SUBDIRS projectCan generate "library export" pri files that provide an easy and reliable way to link against librariesImplicitly supports exported qdep packages and projectsDesign Goals/Scopeqdep was designed with the following goals in mind:Full qmake integration: qdep should not require any extra commands to install packages and prepare a project to be built. Just running qmake on the target project should take care of thisSimple installation: Using python makes it easy to use the tool on any platform. Besides the install only a single setup command is needed to make qdep work for any Qt installationFull Qt support: All features of Qt - including resources, startup hooks and translations should be supported with minimal effort for application developersLibrary exports: Since all qdep packages are source based, having multiple libraries that use the same package can be problematic. With qdep, one can "export" a package from a library, making it available to all the others.No additional server infrastructure: qdep should not require any extra servers to provide a package index. Any git repository can server as a package without any extra preparations neededPlease note that qdep is kept intentionally small to fullfill exactly those goals. Other build systems or more complex package management features are not supported and never will be. This project will stay active until the Qt Company switches their build systems to CMake. At that point this project will either be dropped or ported to CMake, depending on what alternative solutions already exist at that point.InstallationTo install the package, follow one of the following possibilities. Please note that only Python >= 3.7 is officially supported. Earlier versions might work as well, but have not been tested.Arch Linux:Use the AUR-Package:qdepAny Platform:Install via pip:pip install qdepAny Platform:Clone the repository and install the sources directly:python setup.py installPreparing qmakeAfter installing (except when using the AUR-package), you have to "enable" qdep for each Qt-Kit you want to use qdep with. This can be done by opening a terminal and calling:qdepprfgen--qmake"</path/to/qmake>"For example, if you have installed Qt 5.12.0 for MSVC 2017 x64 on windows in the default location (C:\Qt), the command would look like this:qdepprfgen--qmake"C:\Qt\5.12.0\msvc2017_64\bin\qmake.exe"Note:Depending on how the corresponding Qt-Kit was installed, you might need to run the command with administrator/sudo permissions. Alternatively, you can call the command with--dir /some/pathand export that very same path as value to theQMAKEPATHenvironment variable, if you have no such permissions.Shell completitionsFor Unix systems, qdep makes use ofargcompleteto provide completitions for bash/zsh to activate them, add the following code your shell initializer scripts:Forzsh, add this to~/.zshrc:autoloadbashcompinit
bashcompinit
autoloadcompinit
compiniteval"$(register-python-argcompleteqdep)"Forbash, add this to~/.bashrc:eval "$(register-python-argcomplete qdep)"When using BASH, you can alternatively use global completition - seeActivating global completionfor more details on that. Other shells might work as well, depending on how well argcomplete works with them. Refer to the argcomplete documentation and their GitHub Repository .Getting startedThe basic usage of qdep is very simple. For this example, we assume you want to add for exampleQHotkeyto a project via qdep. All you have to do is to install (and prepare) qdep and then add the following two lines to your pro-file:QDEP_DEPENDS += Skycoder42/QHotkey
!load(qdep):error("Failed to load qdep feature")Thats it! The next time you run qmake qdep will automatically download the latest release and add them to your project. Just compile the project and you can use the library. A more explicit way to specify the package (and what that shorter string extends to) would behttps://github.com/Skycoder42/QHotkey.git@1.2.2/qhotkey.priGetting deeperBesides this basic functionality of referencing qdep packages, qdep offers a few additional things to make using (and developing) those easier. In the following sections, they will be explained in detail.Dependency IDsQdep dependencies are described by IDs. These IDs follow the format<url>[@<version>[/<path>]]. The only relevant part is the URL, and it can either be implicit or explicit. Implicit URLs follow the format<user>/<repository>and are automatically expanded tohttps://github.com/<user>/<repository>.git. For the explicit format, all kinds of GIT-URLs are supportet, i.e. HTTPS, SSH and FILE urls.If you leave out the path part of a qdep package, qdep assumes that there is a pri file named<repository_lower>.priin the repositories root directory. If that is not the case, or if a package has multiple different pri files to choose from, you can specify a path relative to the repositories root to that pri file to use that one instead of the automatically detected one.VersioningQdep supports 3 kinds of versioning: Unversioned, branches, tags. If you leave out the version, qdep will automatically query the corresponding repository and get the latest tag (by "creation") and use that one. If you do specify a version, it can either be a git tag or a git branch. In case of a tag, qdep will simply download it and assume it to be persistant, i.e. never check for updates on that specific tag. Referencing a branch however will lead to qdep "tracking" that branch, and before every build, qdep pulls on the branch to update if neccessary.Generelly speaking, it is recommended to use explicit tags. Implicit versioning is fine, too, but updates to packages might break your builds at times you do not want them to. Branch versioning is always dangerous and should only be used on explicitly stable branches or for package delevopment.Package-uniqueness and version conflictsWhen working with recursive package dependencies, it can sometimes happen that two different packages include different versions of the same package. qdep circumvents this issue by making shure only a single version of each package is ever included. The first version this is referenced is the one that is choosen. Explicit package paths however are not considered the same package, i.e. it is possible to include two different pri files from the same git repository. Generelly speaking, a packages unique identifier is determined by its<url>and its<path>- both case insensitive.Normal dependenciesNormal dependencies, aka pri-dependencies specified viaQDEP_DEPENDSare the primary dependency type of qdep. They are typically resolve to a simple pri files, that is included into your project by qdep. You can do anything in these pri files you would also in a "normal" pri file. However, there are a few extra things that become possible when including qdep dependencies.TranslationsThe first feature is extended support for translations. Qdep packages can come with translation source files for their own sources. These are typically exported via theQDEP_TRANSLATIONSqmake variable. When creating your own translations, qdep will automatically merge these with your own translations at build time. This however onyl works if you make use of thelreleaseqmake feature, provided by qt. SeeQMake TRANSLATIONSandQMake QM_FILES_INSTALL_PATfor more details.Library supportWhen using qdep in static or dynamic libraries, there are often special steps needed to make that fully work. However, qdep takes care of those steps and performs them for you. The only thing you need to do is enable library exports from your library and then import that export from your primary project. For example, assuimg you have the following project structure:root (subdirs)
 |-library (lib)
 |-app (app)And you have a library that depends on QHotkey. If you want to use this library from app, you would create the library pro file as follows:TEMPLATE = lib
CONFIG += static  # dynamic libraries are also possible, but dependencies must support exporting for them to work on windows

# ...

QDEP_DEPENDS += Skycoder42/QHotkey
QDEP_EXPORTS += Skycoder42/QHotkey
CONFIG += qdep_link_export
!load(qdep):error("Failed to load qdep feature")And then reference the library in the app pro file. This also takes care of linking the library to the app, so no additionalINCLUDEPATHorLIBSchanges are needed:TEMPLATE = app

# ...

QDEP_LINK_DEPENDS += ../library
!load(qdep):error("Failed to load qdep feature")And thats it! You can now use the QHotkey in the library and the app project without any additional work, as qdep will reference the QHotkey that is now embedded into the library project.Note:This will also work for dynamic librabries, but only if theexplicitlysupport qdep package exporting. If not, linking will fail at least on windows, and possibly on other platforms as well.Creating normal dependenciesThis section is intended for developers that want to create their own qdep packages. Generally speaking, there is not much you need to do different from creating normal pri includes. However, there are a few small things in qdep you can use to your advantage to create better packages. They are described in the following sub sections and are:Translation generationResources and startup hooksautomatic exportsCreating qdep translationsThe only difference when creating translations with qdep is where you put them. Instead of TRANSLATIONS, create a qmake variable called QDEP_TRANSLATIONS and add all the ts files you want to generate to it. Next, call the following command to actually generate the ts-files based on your pri file:qdeplupdate--qmake"</path/to/qmake>"--pri-file"</path/to/project.pri>"[--<extralupdatearguments>...]And thats it. You should now be able to find the generated TS-files where you wanted them to be as specified in the pri file.When creating packages that should work with and without qdep, you can add the following to your pri file to still make these translations available if the package is included without qdep:!qdep_build: EXTRA_TRANSLATIONS += $$QDEP_TRANSLATIONSResources and hooksOne thing that can be problematic, especially when working with static libraries, is the use of RESOURCES and startup hooks. To make it work, qdep automatically generates code to load them. For resources, there is nothing special you need to do as package developer.For hooks however, thats a different story. Assuming you have the following function you want to be run asQ_COREAPP_STARTUP_FUNCTION:voidmy_package_startup_hook(){doStuff();}You will have to add the following line to your qdep pri file to make shure this hook actually gets called:QDEP_HOOK_FNS += my_package_startup_hookAnd with that, qdep will automatically generate the code needed to call this method asQ_COREAPP_STARTUP_FUNCTION.When creating packages that should work with and without qdep, you can add the following to the cpp file that contains the hook function to make it work for non-static projects, even if the package is included without qdep:#ifndef QDEP_BUILD#include<QCoreApplication>Q_COREAPP_STARTUP_FUNCTION(my_package_startup_hook)#endifAutomatic exportsAnother very useful tool are automatic package exports. This allows qdep to automatically export a qdep package from a dynamic library, so other applications that link to that library can use the exported qdep packages API. This is basically equivalent to the following:#ifdef BUILD_PACKAGE_AS_LIBRARY#ifdef IS_DLL_BUILD#define MY_PACKAGE_EXPORT Q_DECL_EXPORT#else#define MY_PACKAGE_EXPORT Q_DECL_IMPORT#endif#else#define MY_PACKAGE_EXPORT#endifclassMY_PACKAGE_EXPORTMyClass{// ...};qdep basically automates the define part, so you don't have to care about correctly defining all those macros and your code can be reduced to:classMY_PACKAGE_EXPORTMyClass{// ...};To make it work, simply add the following to your pri file:QDEP_PACKAGE_EXPORTS += MY_PACKAGE_EXPORTAnd thats it! When using the package normally, qdep will automatically add an empty define that defines MY_PACKAGE_EXPORT to nothing. When building a dynamic library and the end user wants to export the package, it gets defined as Q_DECL_EXPORT (and Q_DECL_IMPORT for consuming applications).When creating packages that should work with and without qdep, you can add the following to the pri file to manually define the macro to nothing, even if the package is included without qdep:!qdep_build: DEFINES += "MY_PACKAGE_EXPORT="Project dependenciesAnother side of qdep besides normal pri dependencies are full project dependencies. In this scenario, you include a full qmake project into your tree as child of a SUBDIRS projects. This allows the use of qdep package export pri files to link to these projects without much effort. To make use of this feature, you have to use a subdirs project that references the pro dependency, as well as normal projects that link against it. One great advantage of using project dependencies is, that they can reference other project dependencies they depend on, which means even for full projects, qdep takes care of the recursive dependency resolving for you.To get started, assume the following project structure:root (subdirs)
  |--libs (subdirs)
  |    |--lib (lib)
  |--app (app)Both lib and app are assumed to depend on a theoretical qdep project dependency nameSkycoder42/SuperLib, but app also depends on lib.The first step would be to choose a subdirs project to add the dependency to. For this example, the libs project is choosen. Add the following lines to add the dependency:TEMPLATE = subdirs
SUBDIRS += lib

# ....

QDEP_PROJECT_SUBDIRS += Skycoder42/SuperLib
lib.qdep_depends += Skycoder42/SuperLib
!load(qdep):error("Failed to load qdep feature")TheQDEP_PROJECT_SUBDIRSis used to actually pull in the project dependency, while adding it tolib.qdep_dependsonlymakes sure that the qdep dependency is built before lib. This is not needed if lib does not depend on the qdep dependency. It is however recommended to always have a seperate subdirs project for qdep dependencies, i.e. for this concrete example it would be better to move the lib project one stage up or create another subdir project within libs that references the qdep dependencies.Next, we need to reference the library itself in app/lib. The procedure is the same for both, so here it is only shown for the app project as an example. In the app pro file, add the lines:QDEP_PROJECT_ROOT = libs  # Or "./libs" - would be ".." for the lib project
QDEP_PROJECT_LINK_DEPENDS += Skycoder42/SuperLib
!load(qdep):error("Failed to load qdep feature")QDEP_PROJECT_ROOTtells qdep where the project is located, that contains the reference to the actual qdep project dependency, andQDEP_PROJECT_LINK_DEPENDSlist all the dependencies this project (app) depends on. If any dependency listed there was not specified in the libs project viaQDEP_PROJECT_SUBDIRS, the build will fail.And with that, the project dependency has been sucessfully added and referenced. With the next build, the project would be downloaded, compiled and linked against app/lib.Creating project dependenciesGenerally speaking, project dependencies are just normal qmake projects. However, such a project shouldalwaysinclude the qdep feature and addqdep_link_exportto the config, as without the generated export pri file, it will not be usable as qdep project dependency. But besides that, you can do anything you want, i.e. add other normal qdep pri dependencies etc. and even export them if needed.However, there is one additional feature that is only possible with qdep project dependencies: You can directly reference other qdep project dependencies. Doing so will make sure that whichever subdirs project that includes this project will also include the dependencies as subdirs and ensure the qmake build order, as well as referencing the corresponding export pri files. To for example referenceSkycoder42/SuperLibfrom within a qdep project dependency, add the following:QDEP_PROJECT_DEPENDS += Skycoder42/SuperLib
!load(qdep):error("Failed to load qdep feature")DocumentationIn the following sections, all the functions, variables etc. of qdep are documented for reference.Command line interfaceqdep has a public and a private command line API. The public API is intended to be used by delevopers, while the internal API is used by the qdep qmake feature to perform various operations. The following sections list all the commands with a short description. For more details on each command, typeqdep <command> --helpPublic API operations:prfgen     Generate a qmake project feature (prf) for the given qmake.
init       Initialize a pro file to use qdep by adding the required lines.
lupdate    Run lupdate for the QDEP_TRANSLATION variable in a given pri
           file.
clear      Remove all sources from the users global cache.
versions   List all known versions/tags of the given package
query      Query details about a given package identifier
get        Download the sources of one ore more packages into the source
           cache.
update     Check for newer versions of used packages and optionally update
           them.Private API operations:dephash     Generated unique identifying hashes for qdep
            packages.
pkgresolve  Download the given qdep package and extract relevant
            information from it.
hookgen     Generate a header file with a method to load all
            resource hooks.
hookimp     Generate a source file that includes and runs all
            qdep hooks as normal startup hook.
lconvert    Combine ts files with translations from qdep
            packages.
prolink     Resolve the path a linked project dependency would
            be at.QMAKE-FeatureThis is the documentation of the qmake feature that is generated by qdep and loaded by addingload(qdep)to your project. All variables, CONFIG-flags and more are documented below.VariablesCommon VariablesNameDirectionDefaultDescriptionsQDEP_PATHin/out<system>Holds the path to the qdep binary to be used. Can be overwritten to specify a custom locationsQDEP_VERSIONout<system>The version of qdep which was used to generate the qdep featureQDEP_GENERATED_DIRin$$OUT_PWDThe sub-directory in the build folder where qdep should place all it's generated files. Can be an absolute or relative pathQDEP_EXPORT_PATHin$$QDEP_GENERATED_DIR/<type>The directory where to place export pri files for libraries that export dependencies. Can be relative to OUT_PWD or absolute. Use QDEP_EXPORT_NAME to get the name of that file without a pathQDEP_DEPENDSin<empty>Specify all dependencies to qdep packages to be included into your projectQDEP_LINK_DEPENDSin<empty>Reference other projects in the same build tree that this project should link against. Those projects must be exporting a pri fileQDEP_PROJECT_SUBDIRSin<empty>Specify all dependencies to qdep projects to be added as qmake project to the SUBDIRS variable. Only evaluted in projects that specify TEMPLATE assubdirsQDEP_PROJECT_ROOTin<empty>The path to a project qmake project directory or file to resolve QDEP_PROJECT_LINK_DEPENDS againstQDEP_PROJECT_LINK_DEPENDSin<empty>Sepcify all dependencies to qdep projects this project should be linked against. The dependencies are assumed to be provided in that project via QDEP_PROJECT_SUBDIRS.QDEP_DEFINESin<empty>Specify DEFINES that are exported if the library exports a pri file. All values are automatically added to DEFINES by qdepQDEP_INCLUDEPATHin<empty>Specify INCLUDEPATHS that are exported if the library exports a pri file. All values are automatically added to INCLUDEPATH by qdepQDEP_LIBSin<empty>Specify LIBS that are exported if the library exports a pri file. All values are automatically added to LIBS by qdepQDEP_EXPORTSin<empty>Specify qdep dependencies of which the API should be exported. Can be useful to export packages when used in dynamic libraries - only works if packages explicitly support thisQDEP_LUPDATE_INPUTSin<empty>Additional files or folders to parse for translations when runningmake lupdateQDEP_PACKAGE_EXPORTSin (pkg)<empty>Variables to be defined as import/export/nothing and be used as prefix to a class.QDEP_TRANSLATIONSin (pkg)<empty>Specify translation subfiles within a qdep dependency to be merged with the translations of the project they are included fromQDEP_PROJECT_DEPENDSin (pkg)<empty>Specify all dependencies to qdep projects this qdep project depends on. Can only be used in qdep project dependencies and adds it's contents to QDEP_PROJECT_SUBDIRS of the project that includes this dependencyQDEP_VAR_EXPORTSin (pkg)<empty>Specify the names of additional qmake variables that should be exported from a qdep dependency besides DEFINES and INCLUDEPATHSUBDIRS extensionqdep_depends: Can be added to any variable passed toSUBDIRSin a project that usesQDEP_PROJECT_SUBDIRSto specify that a certain subdir project depends on that specific package. This doesnottake care of linkage etc. It only ensures that the make targets are build in the correct order.Advanced VariablesNameDirectionDefaultDescriptionsQDEP_TOOLin/out<system>The escaped command line base for qdep commands run from within qmakeQDEP_CACHE_SCOPEinstashThe method of caching to be used to cache various qmake related stuff. Can be <empty>, super or stashQDEP_GENERATED_SOURCES_DIRout$$QDEP_GENERATED_DIR/<type>The directory where generated source files are placed. Is determined by the build configuration to take debug/release builds into accountQDEP_GENERATED_TS_DIRin$$QDEP_GENERATED_DIR/.qdepts/<type>The directory where generated translation sources are placed.QDEP_LUPDATEinlupdate -recursive -locations relativeThe path to the lupdate tool and additional arguments for lupdate in themake lupdatecommand to control it's behaviourQDEP_LCONVERTinlconvert -sort-contextsThe path to the lconvert tool and additional arguments to be used to combine translationsQDEP_EXPORT_NAMEin/out<pro-file-name>_export.priThe name of a generated library import file. Must be only the name of the file, use QDEP_EXPORT_PATH to specify the locationQDEP_EXPORTED_DEFINESout<empty>DEFINES that come from QDEP_PACKAGE_EXPORTS or DEFINES from any directly included qdep dependencyQDEP_EXPORTED_INCLUDEPATHout<empty>INCLUDEPATHs that come from any directly included qdep dependencyQDEP_EXPORTED_LIBSout<empty>LIBS that come from any directly included qdep dependencyQDEP_HOOK_FNSout<empty>Holds all names of functions to be run as startup hook by qdepConfiguration valuesInput valueqdep_force: Enforce the full evaluation of qdep, even if the pro file is beeing evaluated without a context (This is potentially dangerous and should not be used unless absolutely neccessary)qdep_no_pull: Do not pull for newer versions of branch-based dependencies. Cloning new packages will still workqdep_no_clone: Do not clone completely new packages or package versions. Pulling on already cached branches will still workqdep_no_cache: Do not cache the used versions of packages without a specified versions. Instead the latest version is queried on every runqdep_export_all: export all dependant packages, i.e. any QDEP_PACKAGE_EXPORTS for every package are treated as if added to QDEP_EXPORTSqdep_no_link: When exporting packages from a library, do not add the qmake code to link the library (and to it's includes) to the generate export.pri fileqdep_no_qm_combine: Do not combine TRANSLATIONS with QDEP_TRANSLATIONS. Instead treat QDEP_TRANSLATIONS as EXTRA_TRANSLATIONS and generate seperate qm files for themqdep_link_export: Enforce the creation of an export pri file. Normally only libraries without qdep_no_link defined or projects that specify qdep_export_all or have values in QDEP_EXPORTS generate such filesqdep_no_export_link: Do not export contents of QT, PKGCONFIG or QDEP_LIBS in a generated export pri fileOutput valuesqdep_build: Is set in case the qdep features was loaded correctly and is enabledDefinesQDEP_BUILD: Is defined in case the qdep features was loaded correctly and is enabledEnvironment variablesQDEP_CACHE_DIR: The directory where to cache downloaded sources. Is automatically determined for every system but can be overwritten with this variableQDEP_SOURCE_OVERRIDE: Allows to provide a mapping in the format<pkg1>;<pkg2>^<pkg3>;<pkg4>. This will make qdep automatically replace any occurance ofpkg1withpkg2etc. Can be used by developers to temporarily overwrite packagesQDEP_DEFAULT_PKG_FN: A template that is used to resolve non-url packages likeUser/packageto a full url. The default method for that ishttps://github.com/{}.git- with{}being replaced by the short package name.Public make targetsmake lupdate: Runs thelupdatetool to update your translations in a quick and easy manner, without having to rely on potentially buggy pro-file-parsing.InternalVariables__QDEP_PRIVATE_SEPERATOR: Seperator between datasets__QDEP_TUPLE_SEPERATOR: Seperator between values within a dataset__QDEP_INCLUDE_CACHE: objects with details about included depdendencies of all kinds__QDEP_ORIGINAL_TRANSLATIONS: All original translation that were in TRANSLATIONS before apply translation combination with QDEP_TRANSLATIONS from included packages__QDEP_HOOK_FILES: Paths to header files that contain hook definitions that must be included and loaded by a project__QDEP_EXPORT_QMAKE_INCLUDE_GUARD: a cache of included exported pri files to ensure each gets included only onceConfiguration values__qdep_script_version: The version detected at runtime as reported by the qdep executable__qdep_dump_dependencies: Create a file named qdep_depends.txt in the build directory that contains all direct dependencies of the projectQMAKE test functionsqdepCollectDependencies(dependencies ...): Downloads all specified normal dependencies and adds them to__QDEP_INCLUDE_CACHEto be linked later by qdep. Works recursivelyqdepCollectProjectDependencies(dependencies ...): Downloads all specified project dependencies and adds them toSUBDIRS. Works recursivelyqdepResolveSubdirDepends(subdir-vars ...): Converts the values of theqdep_dependssubvar of all variables passed to the function to normal subdirsdependssubvarsqdepCreateExportPri(path): Creates a file named path and adds all export-related code to itqdepShellQuote(paths ...): Makes the given paths absolute based on_PRO_FILE_PWD_and escapes them usingshell_quoteqdepDumpUpdateDeps(): Creates a file named qdep_depends.txt in the build directory that contains all direct dependencies of the projectQMAKE replace functionsqdepResolveProjectLinkDeps(project-root, link-depends ...): Resolves all link-depends project packages to subdir paths, assuming they are provided by the project located at project-rootqdepOutQuote(name, values): Creates a number of lines of qmake code that assing each value in values to a variable named name, in a securely quoted mannerqdepLinkExpand(path): Expands a shortened or relative path to a project that exports dependencies to get the path of the export pri fileqdepResolveLinkRoot(path): Searches for a top-level qmake project, that has this project included as project dependency and returns the path to that pro file. Search is started based on path, which must be a build directory