├── qdep
├── __init__.py
├── internal
│ ├── __init__.py
│ ├── private.py
│ ├── common.py
│ ├── cli.py
│ └── prf.py
└── qdep.py
├── tests
├── .gitignore
├── project
│ ├── external
│ │ ├── libstatic
│ │ │ ├── static.txt
│ │ │ ├── super-static.qrc
│ │ │ ├── extra
│ │ │ │ └── extra_sta.h
│ │ │ ├── libstatic.h
│ │ │ ├── libstatic.cpp
│ │ │ └── libstatic.pro
│ │ ├── libdynamic
│ │ │ ├── extra
│ │ │ │ └── extra_dyn.h
│ │ │ ├── libdynamic.cpp
│ │ │ ├── libdynamic.h
│ │ │ └── libdynamic.pro
│ │ ├── external.pro
│ │ └── app
│ │ │ ├── main.cpp
│ │ │ └── app.pro
│ ├── mixdeps
│ │ ├── lib3
│ │ │ ├── lib3.cpp
│ │ │ ├── lib3.h
│ │ │ └── lib3.pro
│ │ ├── lib1
│ │ │ ├── lib1.h
│ │ │ ├── lib1.cpp
│ │ │ └── lib1.pro
│ │ ├── lib2
│ │ │ ├── lib2.cpp
│ │ │ ├── lib2.h
│ │ │ └── lib2.pro
│ │ ├── app
│ │ │ ├── main.cpp
│ │ │ └── app.pro
│ │ └── mixdeps.pro
│ ├── .qmake.conf
│ ├── libs
│ │ ├── app
│ │ │ ├── main.cpp
│ │ │ └── app.pro
│ │ └── libs.pro
│ ├── project.pro
│ ├── commands
│ │ ├── commands.pro
│ │ └── test-commands.py
│ ├── basic
│ │ ├── main.cpp
│ │ └── basic.pro
│ ├── single
│ │ ├── single_de.ts
│ │ ├── single_ja.ts
│ │ ├── single.pro
│ │ └── main.cpp
│ ├── tests.h
│ └── testrun.pri
├── packages
│ ├── external
│ │ ├── package2
│ │ │ ├── package2.txt
│ │ │ ├── package2.qrc
│ │ │ ├── staticclass.h
│ │ │ ├── package2.pri
│ │ │ └── staticclass.cpp
│ │ ├── package1
│ │ │ └── package1.pri
│ │ ├── package3
│ │ │ └── package3.pri
│ │ ├── package4
│ │ │ └── package4.pri
│ │ └── package5
│ │ │ ├── dynamicclass.h
│ │ │ ├── package5.pri
│ │ │ └── dynamicclass.cpp
│ ├── basic
│ │ ├── package4
│ │ │ └── package4.pri
│ │ ├── package5
│ │ │ └── package5.pri
│ │ ├── package1
│ │ │ ├── simple.cpp
│ │ │ ├── package1.pri
│ │ │ ├── package1_de.ts
│ │ │ ├── package1_en.ts
│ │ │ └── simple.h
│ │ ├── package2
│ │ │ └── package2.pri
│ │ └── package3
│ │ │ └── package3.pri
│ ├── libs
│ │ ├── project1
│ │ │ ├── project1.cpp
│ │ │ ├── project1.h
│ │ │ └── project1.pro
│ │ ├── project3
│ │ │ ├── project3.h
│ │ │ ├── project3.cpp
│ │ │ └── project3.pro
│ │ └── project2
│ │ │ ├── project2.cpp
│ │ │ ├── project2.h
│ │ │ └── project2.pro
│ └── mixdeps
│ │ ├── project2
│ │ ├── project2.cpp
│ │ ├── project2.h
│ │ └── project2.pro
│ │ ├── project3
│ │ ├── project3.cpp
│ │ ├── project3.h
│ │ └── project3.pro
│ │ ├── project4
│ │ ├── project4.cpp
│ │ ├── project4.h
│ │ └── project4.pro
│ │ ├── project1
│ │ ├── project1.cpp
│ │ ├── project1.h
│ │ └── project1.pro
│ │ └── project5
│ │ ├── project5.cpp
│ │ ├── project5.h
│ │ └── project5.pro
├── tests.pro
├── qdep.pro
├── testentry.py
├── setup-docker.sh
├── setup-appveyor.bat
├── setup-travis.sh
└── testrun.sh
├── appveyor.yml
├── .gitignore
├── setup.py
├── .travis.yml
├── LICENSE
└── README.md
/qdep/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/qdep/internal/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/.gitignore:
--------------------------------------------------------------------------------
1 | .qdep
2 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/static.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/packages/external/package2/package2.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/packages/basic/package4/package4.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package4_included
2 |
--------------------------------------------------------------------------------
/tests/packages/basic/package5/package5.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package5_included
2 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib3/lib3.cpp:
--------------------------------------------------------------------------------
1 | #include "lib3.h"
2 |
3 | void Lib3::lib3()
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/tests/project/.qmake.conf:
--------------------------------------------------------------------------------
1 | load(qt_build_config)
2 | CONFIG -= create_prl link_prl
3 | CONFIG += c++17
--------------------------------------------------------------------------------
/tests/packages/external/package1/package1.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package1_included
2 | DEFINES += PACKAGE1_DEFINED
3 |
--------------------------------------------------------------------------------
/tests/packages/libs/project1/project1.cpp:
--------------------------------------------------------------------------------
1 | #include "project1.h"
2 |
3 | void Project1::doStuff()
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project2/project2.cpp:
--------------------------------------------------------------------------------
1 | #include "project2.h"
2 |
3 | void Project2::doStuff()
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project3/project3.cpp:
--------------------------------------------------------------------------------
1 | #include "project3.h"
2 |
3 | void Project3::doStuff()
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project4/project4.cpp:
--------------------------------------------------------------------------------
1 | #include "project4.h"
2 |
3 | void Project4::doStuff()
4 | {
5 | }
6 |
--------------------------------------------------------------------------------
/tests/tests.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += project
4 |
5 | prepareRecursiveTarget(run-tests)
6 | QMAKE_EXTRA_TARGETS += run-tests
7 |
--------------------------------------------------------------------------------
/tests/qdep.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | SUBDIRS += tests
4 |
5 | prepareRecursiveTarget(run-tests)
6 | QMAKE_EXTRA_TARGETS += run-tests lrelease
7 |
--------------------------------------------------------------------------------
/tests/packages/external/package2/package2.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | package2.txt
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/super-static.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | static.txt
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/testentry.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 |
4 | from qdep.internal.cli import main
5 |
6 | if __name__ == '__main__':
7 | sys.exit(main())
8 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project1/project1.cpp:
--------------------------------------------------------------------------------
1 | #include "project1.h"
2 | #include
3 |
4 | void Project1::doStuff()
5 | {
6 | Project2::doStuff();
7 | }
8 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project5/project5.cpp:
--------------------------------------------------------------------------------
1 | #include "project5.h"
2 | #include
3 |
4 | void Project5::doStuff()
5 | {
6 | Project3::doStuff();
7 | }
8 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib1/lib1.h:
--------------------------------------------------------------------------------
1 | #ifndef LIB1_H
2 | #define LIB1_H
3 |
4 | class Lib1
5 | {
6 | public:
7 | static void lib1();
8 | };
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib3/lib3.h:
--------------------------------------------------------------------------------
1 | #ifndef LIB3_H
2 | #define LIB3_H
3 |
4 | class Lib3
5 | {
6 | public:
7 | static void lib3();
8 | };
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/tests/packages/libs/project1/project1.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT1_H
2 | #define PROJECT1_H
3 |
4 | namespace Project1 {
5 |
6 | void doStuff();
7 |
8 | }
9 |
10 | #endif
--------------------------------------------------------------------------------
/tests/packages/libs/project3/project3.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT3_H
2 | #define PROJECT3_H
3 |
4 | namespace Project3 {
5 |
6 | void doStuff();
7 |
8 | }
9 |
10 | #endif
--------------------------------------------------------------------------------
/tests/packages/external/package3/package3.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package3_included
2 | DEFINES += PACKAGE3_DEFINED
3 |
4 | FUNNY_STUFF += baum42
5 | QDEP_VAR_EXPORTS += FUNNY_STUFF
6 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project1/project1.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT1_H
2 | #define PROJECT1_H
3 |
4 | namespace Project1 {
5 |
6 | void doStuff();
7 |
8 | }
9 |
10 | #endif
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project2/project2.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT2_H
2 | #define PROJECT2_H
3 |
4 | namespace Project2 {
5 |
6 | void doStuff();
7 |
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project4/project4.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT4_H
2 | #define PROJECT4_H
3 |
4 | namespace Project4 {
5 |
6 | void doStuff();
7 |
8 | }
9 |
10 | #endif
11 |
--------------------------------------------------------------------------------
/tests/packages/external/package4/package4.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package4_included
2 | DEFINES += PACKAGE4_DEFINED
3 |
4 | FUNNY_STUFF += baum24
5 | QDEP_VAR_EXPORTS += FUNNY_STUFF
6 |
7 | INCLUDEPATH += $$PWD
8 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib1/lib1.cpp:
--------------------------------------------------------------------------------
1 | #include "lib1.h"
2 | #include
3 | #include
4 |
5 | void Lib1::lib1()
6 | {
7 | Project1::doStuff();
8 | Project3::doStuff();
9 | }
10 |
--------------------------------------------------------------------------------
/tests/project/external/libdynamic/extra/extra_dyn.h:
--------------------------------------------------------------------------------
1 | #ifndef EXTRA_DYN_H
2 | #define EXTRA_DYN_H
3 |
4 | #include
5 |
6 | template
7 | T dummy_dyn(T &&data) { return std::move(data); }
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/extra/extra_sta.h:
--------------------------------------------------------------------------------
1 | #ifndef EXTRA_STA_H
2 | #define EXTRA_STA_H
3 |
4 | #include
5 |
6 | template
7 | T dummy_sta(T &&data) { return std::move(data); }
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/tests/packages/libs/project3/project3.cpp:
--------------------------------------------------------------------------------
1 | #include "project3.h"
2 | #include
3 |
4 | void Project3::doStuff()
5 | {
6 | Simple simple;
7 | simple.value = QStringLiteral("TREE42");
8 | simple.transform();
9 | }
10 |
--------------------------------------------------------------------------------
/tests/packages/basic/package1/simple.cpp:
--------------------------------------------------------------------------------
1 | #include "simple.h"
2 |
3 | QString Simple::transform() const
4 | {
5 | return value.toLower();
6 | }
7 |
8 | QString Simple::translate() const
9 | {
10 | return tr("Hello World");
11 | }
12 |
--------------------------------------------------------------------------------
/tests/packages/basic/package2/package2.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package2_included
2 |
3 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package3/package3.pri
4 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package4/package4.pri
5 |
--------------------------------------------------------------------------------
/tests/packages/basic/package3/package3.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package3_included
2 |
3 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package4/package4.pri
4 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package5/package5.pri
5 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib2/lib2.cpp:
--------------------------------------------------------------------------------
1 | #include "lib2.h"
2 | #include
3 | #include
4 | #include
5 |
6 | void Lib2::lib2()
7 | {
8 | Lib3::lib3();
9 | Project4::doStuff();
10 | Project5::doStuff();
11 | }
12 |
--------------------------------------------------------------------------------
/tests/project/libs/app/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | int main(int argc, char **argv)
6 | {
7 | QCoreApplication app{argc, argv};
8 | Project2::doStuff();
9 |
10 | return 0;
11 | }
12 |
--------------------------------------------------------------------------------
/tests/packages/external/package2/staticclass.h:
--------------------------------------------------------------------------------
1 | #ifndef STATICCLASS_H
2 | #define STATICCLASS_H
3 |
4 | #include
5 |
6 | class PACKAGE2_EXPORT StaticClass
7 | {
8 | public:
9 | static bool startupRun();
10 |
11 | int magicNumber();
12 | };
13 |
14 | #endif
--------------------------------------------------------------------------------
/tests/packages/external/package5/dynamicclass.h:
--------------------------------------------------------------------------------
1 | #ifndef DYNAMICCLASS_H
2 | #define DYNAMICCLASS_H
3 |
4 | #include
5 |
6 | class PACKAGE5_EXPORT DynamicClass
7 | {
8 | public:
9 | static bool startupRun();
10 | int magicNumber();
11 | };
12 |
13 | #endif
--------------------------------------------------------------------------------
/tests/project/project.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | CONFIG += ordered
4 |
5 | SUBDIRS += \
6 | single \
7 | basic \
8 | external \
9 | libs \
10 | mixdeps \
11 | commands
12 |
13 | prepareRecursiveTarget(run-tests)
14 | QMAKE_EXTRA_TARGETS += run-tests lrelease
15 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/libstatic.h:
--------------------------------------------------------------------------------
1 | #ifndef LIBSTATIC_H
2 | #define LIBSTATIC_H
3 |
4 | #include
5 |
6 | class LibStatic : public StaticClass
7 | {
8 | public:
9 | static bool libStartupRun();
10 |
11 | double magicFraction();
12 | };
13 |
14 | #endif
--------------------------------------------------------------------------------
/tests/project/mixdeps/app/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | int main(int argc, char **argv)
7 | {
8 | QCoreApplication app{argc, argv};
9 | Lib1::lib1();
10 | Lib2::lib2();
11 |
12 | return 0;
13 | }
14 |
--------------------------------------------------------------------------------
/tests/project/external/external.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | CONFIG += ordered
4 |
5 | SUBDIRS += \
6 | libstatic \
7 | libdynamic \
8 | app
9 |
10 | app.depends += libstatic libdynamic
11 |
12 | prepareRecursiveTarget(run-tests)
13 | QMAKE_EXTRA_TARGETS += run-tests
14 |
--------------------------------------------------------------------------------
/tests/packages/libs/project2/project2.cpp:
--------------------------------------------------------------------------------
1 | #include "project2.h"
2 | #include
3 | #include
4 |
5 | void Project2::doStuff()
6 | {
7 | Simple simple;
8 | simple.value = QStringLiteral("BAUM42");
9 | simple.transform();
10 | Project3::doStuff();
11 | }
12 |
--------------------------------------------------------------------------------
/tests/project/commands/commands.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = aux
2 |
3 | run_tests.target = run-tests
4 | win32: run_tests.commands = python
5 | run_tests.commands += $$PWD/test-commands.py $$QMAKE_QMAKE $(MAKE)
6 | local_test_run: run_tests.commands += --testrun $$TEST_RUN_ARGS
7 | QMAKE_EXTRA_TARGETS += run_tests
8 |
--------------------------------------------------------------------------------
/tests/packages/basic/package1/package1.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package1_included
2 | DEFINES += PACKAGE1_DEFINED
3 |
4 | HEADERS += \
5 | $$PWD/simple.h
6 |
7 | SOURCES += \
8 | $$PWD/simple.cpp
9 |
10 | QDEP_TRANSLATIONS += \
11 | $$PWD/package1_de.ts \
12 | $$PWD/package1_en.ts \
13 |
14 | INCLUDEPATH += $$PWD
15 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib2/lib2.h:
--------------------------------------------------------------------------------
1 | #ifndef LIB2_H
2 | #define LIB2_H
3 |
4 | #include
5 |
6 | #ifdef LIB2_BUILD
7 | #define LIB2_EXPORT Q_DECL_EXPORT
8 | #else
9 | #define LIB2_EXPORT Q_DECL_IMPORT
10 | #endif
11 |
12 | class LIB2_EXPORT Lib2
13 | {
14 | public:
15 | static void lib2();
16 | };
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/tests/packages/libs/project1/project1.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = project1
5 |
6 | HEADERS += project1.h
7 |
8 | SOURCES += project1.cpp
9 |
10 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
11 | !load(qdep):error("Failed to load qdep feature")
12 |
13 | QMAKE_EXTRA_TARGETS += run-tests
14 |
--------------------------------------------------------------------------------
/tests/setup-docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # install dependencies
5 | $SUDO pip3 install appdirs lockfile argcomplete setuptools
6 | $SUDO pip3 uninstall -y qdep
7 |
8 | # install/prepare qdep
9 | $SUDO pip3 install -e .
10 | $SUDO qdep prfgen --qmake "/opt/qt/$QT_VER/$PLATFORM/bin/qmake"
11 |
12 | # move project file
13 | mv tests/qdep.pro ./
14 |
--------------------------------------------------------------------------------
/tests/packages/libs/project2/project2.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT2_H
2 | #define PROJECT2_H
3 |
4 | #include
5 |
6 | #ifdef PROJECT2_BUILD
7 | #define PROJECT2_EXPORT Q_DECL_EXPORT
8 | #else
9 | #define PROJECT2_EXPORT Q_DECL_IMPORT
10 | #endif
11 |
12 | namespace Project2 {
13 |
14 | PROJECT2_EXPORT void doStuff();
15 |
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project3/project3.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT3_H
2 | #define PROJECT3_H
3 |
4 | #include
5 |
6 | #ifdef PROJECT3_BUILD
7 | #define PROJECT3_EXPORT Q_DECL_EXPORT
8 | #else
9 | #define PROJECT3_EXPORT Q_DECL_IMPORT
10 | #endif
11 |
12 | namespace Project3 {
13 |
14 | PROJECT3_EXPORT void doStuff();
15 |
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project5/project5.h:
--------------------------------------------------------------------------------
1 | #ifndef PROJECT5_H
2 | #define PROJECT5_H
3 |
4 | #include
5 |
6 | #ifdef PROJECT5_BUILD
7 | #define PROJECT5_EXPORT Q_DECL_EXPORT
8 | #else
9 | #define PROJECT5_EXPORT Q_DECL_IMPORT
10 | #endif
11 |
12 | namespace Project5 {
13 |
14 | PROJECT5_EXPORT void doStuff();
15 |
16 | }
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/tests/project/basic/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 |
6 | int main(int argc, char **argv)
7 | {
8 | QCoreApplication app{argc, argv};
9 |
10 | Simple simple;
11 | simple.value = "Hello World";
12 | COMPARE(simple.transform(), QStringLiteral("hello world"));
13 | return 0;
14 | }
15 |
--------------------------------------------------------------------------------
/tests/packages/external/package5/package5.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package5_included
2 | DEFINES += PACKAGE5_DEFINED
3 |
4 | QDEP_PACKAGE_EXPORTS += PACKAGE5_EXPORT
5 | !qdep_build: DEFINES += "PACKAGE5_EXPORT="
6 |
7 | QDEP_HOOK_FNS += dynamicclass_startup_hook
8 |
9 | HEADERS += $$PWD/dynamicclass.h
10 |
11 | SOURCES += $$PWD/dynamicclass.cpp
12 |
13 | INCLUDEPATH += $$PWD
14 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project3/project3.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | TARGET = project3
4 |
5 | HEADERS += project3.h
6 |
7 | SOURCES += project3.cpp
8 |
9 | DEFINES += PROJECT3_BUILD
10 |
11 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
12 | !load(qdep):error("Failed to load qdep feature")
13 |
14 | QMAKE_EXTRA_TARGETS += run-tests
15 |
--------------------------------------------------------------------------------
/tests/project/single/single_de.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GLOBAL
6 |
7 |
8 | Hello Tree
9 | Hallo Baum
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/project/single/single_ja.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GLOBAL
6 |
7 |
8 | Hello Tree
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/packages/basic/package1/package1_de.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple
6 |
7 |
8 | Hello World
9 | Hallo Welt
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/project/external/libdynamic/libdynamic.cpp:
--------------------------------------------------------------------------------
1 | #include "libdynamic.h"
2 |
3 | namespace {
4 |
5 | bool _startup_run3 = false;
6 |
7 | }
8 |
9 | void libdynamic_startup_hook()
10 | {
11 | _startup_run3 = true;
12 | }
13 |
14 | bool LibDynamic::libStartupRun()
15 | {
16 | return _startup_run3;
17 | }
18 |
19 | double LibDynamic::magicFraction()
20 | {
21 | return 0.24;
22 | }
23 |
--------------------------------------------------------------------------------
/tests/packages/basic/package1/package1_en.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Simple
6 |
7 |
8 | Hello World
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project2/project2.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = project2
5 |
6 | HEADERS += project2.h
7 |
8 | SOURCES += project2.cpp
9 |
10 | QDEP_DEFINES += PROJECT2_DEFINED
11 |
12 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
13 | !load(qdep):error("Failed to load qdep feature")
14 |
15 | QMAKE_EXTRA_TARGETS += run-tests
16 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project4/project4.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = project4
5 |
6 | HEADERS += project4.h
7 |
8 | SOURCES += project4.cpp
9 |
10 | QDEP_DEFINES += PROJECT4_DEFINED
11 |
12 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
13 | !load(qdep):error("Failed to load qdep feature")
14 |
15 | QMAKE_EXTRA_TARGETS += run-tests
16 |
--------------------------------------------------------------------------------
/tests/packages/external/package2/package2.pri:
--------------------------------------------------------------------------------
1 | CONFIG += package2_included
2 | DEFINES += PACKAGE2_DEFINED
3 |
4 | QDEP_PACKAGE_EXPORTS += PACKAGE2_EXPORT
5 | !qdep_build: DEFINES += "PACKAGE2_EXPORT="
6 |
7 | QDEP_HOOK_FNS += staticclass_startup_hook
8 |
9 | HEADERS += $$PWD/staticclass.h
10 |
11 | SOURCES += $$PWD/staticclass.cpp
12 |
13 | RESOURCES += $$PWD/package2.qrc
14 |
15 | INCLUDEPATH += $$PWD
16 |
--------------------------------------------------------------------------------
/tests/project/libs/app/app.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | SOURCES += main.cpp
4 |
5 | QDEP_PROJECT_ROOT = ..
6 | # QDEP_PROJECT_ROOT = ../libs.pro
7 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/qdep@master/tests/packages/libs/project2/project2.pro
8 |
9 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
10 | !load(qdep):error("Failed to load qdep feature")
11 |
12 | include(../../testrun.pri)
13 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/libstatic.cpp:
--------------------------------------------------------------------------------
1 | #include "libstatic.h"
2 |
3 | namespace {
4 |
5 | bool _startup_run2 = false;
6 |
7 | }
8 |
9 | namespace libstatic::hooks {
10 |
11 | void startup_hook()
12 | {
13 | _startup_run2 = true;
14 | }
15 |
16 | }
17 |
18 | bool LibStatic::libStartupRun()
19 | {
20 | return _startup_run2;
21 | }
22 |
23 | double LibStatic::magicFraction()
24 | {
25 | return 0.42;
26 | }
27 |
--------------------------------------------------------------------------------
/tests/packages/basic/package1/simple.h:
--------------------------------------------------------------------------------
1 | #ifndef SIMPLE_H
2 | #define SIMPLE_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class Simple
9 | {
10 | Q_GADGET
11 | Q_DECLARE_TR_FUNCTIONS(Simple)
12 |
13 | Q_PROPERTY(QString value MEMBER value)
14 |
15 | public:
16 | QString value;
17 |
18 | QString transform() const;
19 | QString translate() const;
20 | };
21 |
22 | #endif
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib3/lib3.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | QT += sql
5 |
6 | TARGET = lib3
7 |
8 | HEADERS += lib3.h
9 |
10 | SOURCES += lib3.cpp
11 |
12 | QDEP_DEFINES += LIB3_DEFINED
13 |
14 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
15 | !load(qdep):error("Failed to load qdep feature")
16 |
17 | CONFIG += no_run_tests_target
18 | include(../../testrun.pri)
19 |
20 | message(LIBS: $$LIBS)
21 |
--------------------------------------------------------------------------------
/tests/project/external/libdynamic/libdynamic.h:
--------------------------------------------------------------------------------
1 | #ifndef LIBDYNAMIC_H
2 | #define LIBDYNAMIC_H
3 |
4 | #include
5 | #include
6 |
7 | #ifdef LIBDYNAMIC_BUILD
8 | #define LIBDYNAMIC_EXPORT Q_DECL_EXPORT
9 | #else
10 | #define LIBDYNAMIC_EXPORT Q_DECL_IMPORT
11 | #endif
12 |
13 | class LIBDYNAMIC_EXPORT LibDynamic : public DynamicClass
14 | {
15 | public:
16 | static bool libStartupRun();
17 | double magicFraction();
18 | };
19 |
20 | #endif
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project5/project5.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | TARGET = project5
4 |
5 | HEADERS += project5.h
6 |
7 | SOURCES += project5.cpp
8 |
9 | DEFINES += PROJECT5_BUILD
10 |
11 | QDEP_PROJECT_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project3/project3.pro
12 |
13 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
14 | !load(qdep):error("Failed to load qdep feature")
15 |
16 | QMAKE_EXTRA_TARGETS += run-tests
17 |
--------------------------------------------------------------------------------
/tests/setup-appveyor.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | :: install dependencies
4 | C:\Python37-x64\Scripts\pip.exe install appdirs lockfile argcomplete setuptools || exit /B 1
5 | C:\Python37-x64\Scripts\pip.exe uninstall -y qdep || exit /B 1
6 |
7 | :: install/prepare qdep
8 | C:\Python37-x64\Scripts\pip.exe install -e . || exit /B 1
9 | C:\Python37-x64\Scripts\qdep.exe prfgen --qmake "C:\projects\Qt\%QT_VER%\%PLATFORM%\bin\qmake" || exit /B 1
10 |
11 | :: move project file
12 | move tests\qdep.pro .\dep.pro || exit /B 1
13 |
--------------------------------------------------------------------------------
/tests/packages/external/package2/staticclass.cpp:
--------------------------------------------------------------------------------
1 | #include "staticclass.h"
2 | #include
3 |
4 | namespace {
5 |
6 | bool _startup_run = false;
7 |
8 | }
9 |
10 | void staticclass_startup_hook()
11 | {
12 | _startup_run = true;
13 | }
14 |
15 | #ifndef QDEP_BUILD
16 | Q_COREAPP_STARTUP_FUNCTION(libstatic_startup_hook)
17 | #endif
18 |
19 | bool StaticClass::startupRun()
20 | {
21 | return _startup_run;
22 | }
23 |
24 | int StaticClass::magicNumber()
25 | {
26 | return 422;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/packages/external/package5/dynamicclass.cpp:
--------------------------------------------------------------------------------
1 | #include "dynamicclass.h"
2 | #include
3 |
4 | namespace {
5 |
6 | bool _startup_run4 = false;
7 |
8 | }
9 |
10 | void dynamicclass_startup_hook()
11 | {
12 | _startup_run4 = true;
13 | }
14 |
15 | #ifndef QDEP_BUILD
16 | Q_COREAPP_STARTUP_FUNCTION(dynamicclass_startup_hook)
17 | #endif
18 |
19 | bool DynamicClass::startupRun()
20 | {
21 | return _startup_run4;
22 | }
23 |
24 | int DynamicClass::magicNumber()
25 | {
26 | return 425;
27 | }
28 |
--------------------------------------------------------------------------------
/tests/packages/mixdeps/project1/project1.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = project1
5 |
6 | HEADERS += project1.h
7 |
8 | SOURCES += project1.cpp
9 |
10 | QDEP_PROJECT_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project2/project2.pro
11 |
12 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
13 | !load(qdep):error("Failed to load qdep feature")
14 |
15 | qdeb_build:!contains(DEFINES, PROJECT2_DEFINED): error("!PROJECT2_DEFINED")
16 |
17 | QMAKE_EXTRA_TARGETS += run-tests
18 |
--------------------------------------------------------------------------------
/tests/packages/libs/project3/project3.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = project3
5 |
6 | HEADERS += project3.h
7 |
8 | SOURCES += project3.cpp
9 |
10 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri
11 | QDEP_EXPORTS += Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri
12 |
13 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
14 | !load(qdep):error("Failed to load qdep feature")
15 |
16 | qdep_build:!package1_included: error("!package1_included")
17 |
18 | QMAKE_EXTRA_TARGETS += run-tests
19 |
--------------------------------------------------------------------------------
/tests/setup-travis.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | currDir=$(dirname $0)
5 |
6 | if [[ $TRAVIS_OS_NAME == "linux" ]]; then
7 | # append post build script
8 | mv qtmodules-travis/ci/linux/build-docker.sh qtmodules-travis/ci/linux/build-docker.sh.bkp
9 | cat "$currDir/setup-docker.sh" > qtmodules-travis/ci/linux/build-docker.sh
10 | cat qtmodules-travis/ci/linux/build-docker.sh.bkp >> qtmodules-travis/ci/linux/build-docker.sh
11 | rm qtmodules-travis/ci/linux/build-docker.sh.bkp
12 | chmod a+x qtmodules-travis/ci/linux/build-docker.sh
13 | else
14 | export SUDO=sudo
15 | $currDir/setup-docker.sh
16 | fi
17 |
--------------------------------------------------------------------------------
/tests/project/libs/libs.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/libs/project1/project1.pro
4 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/libs/project2/project2.pro
5 |
6 | SUBDIRS += app
7 |
8 | app.qdep_depends += Skycoder42/qdep@master/tests/packages/libs/project2/project2.pro
9 |
10 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
11 | !load(qdep):error("Failed to load qdep feature")
12 |
13 | CONFIG += no_run_tests_target
14 | prepareRecursiveTarget(run-tests)
15 | include(../testrun.pri)
16 |
17 | message($$SUBDIRS)
18 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib1/lib1.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = lib1
5 |
6 | HEADERS += lib1.h
7 |
8 | SOURCES += lib1.cpp
9 |
10 | QDEP_PROJECT_ROOT = ..
11 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project1/project1.pro
12 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project3/project3.pro
13 |
14 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
15 | !load(qdep):error("Failed to load qdep feature")
16 |
17 | CONFIG += no_run_tests_target
18 | include(../../testrun.pri)
19 |
20 | message(LIBS: $$LIBS)
21 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image:
2 | - Visual Studio 2017
3 |
4 | version: build-{build}
5 |
6 | environment:
7 | QT_VER: 5.13.0
8 | MAKE_RUN_TESTS: true
9 | TARGET_NAME: qdep
10 |
11 | matrix:
12 | - PLATFORM: msvc2017_64
13 | - PLATFORM: mingw73_64
14 | - PLATFORM: winrt_x64_msvc2017
15 | - PLATFORM: msvc2017
16 | - PLATFORM: mingw73_32
17 | - PLATFORM: winrt_x86_msvc2017
18 | - PLATFORM: winrt_armv7_msvc2017
19 |
20 | install:
21 | - git clone https://github.com/Skycoder42/QtModules.git .\qtmodules-travis
22 | - .\qtmodules-travis\ci\win\setup.bat
23 |
24 | build_script:
25 | - .\tests\setup-appveyor.bat
26 | - .\qtmodules-travis\ci\win\build.bat
27 |
--------------------------------------------------------------------------------
/tests/packages/libs/project2/project2.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | TARGET = project2
4 |
5 | HEADERS += project2.h
6 |
7 | SOURCES += project2.cpp
8 |
9 | DEFINES += PROJECT2_BUILD
10 |
11 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri # provided via project3
12 | QDEP_PROJECT_DEPENDS += Skycoder42/qdep@master/tests/packages/libs/project3/project3.pro
13 |
14 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
15 | !load(qdep):error("Failed to load qdep feature")
16 |
17 | qdep_build:package1_included: error("package1_included")
18 | qdeb_build:contains(DEFINES, PACKAGE1_DEFINED): error("!PACKAGE1_DEFINED")
19 |
20 | QMAKE_EXTRA_TARGETS += run-tests
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 | *.slo
3 | *.lo
4 | *.o
5 | *.a
6 | *.la
7 | *.lai
8 | *.so
9 | *.dll
10 | *.dylib
11 |
12 | # Qt-es
13 | object_script.*.Release
14 | object_script.*.Debug
15 | *_plugin_import.cpp
16 | /.qmake.cache
17 | /.qmake.stash
18 | *.pro.user
19 | *.pro.user.*
20 | *.qbs.user
21 | *.qbs.user.*
22 | *.moc
23 | moc_*.cpp
24 | moc_*.h
25 | qrc_*.cpp
26 | ui_*.h
27 | *.qmlc
28 | *.jsc
29 | Makefile*
30 | *build-*
31 |
32 | # Qt unit tests
33 | target_wrapper.*
34 |
35 | # QtCreator
36 | *.autosave
37 |
38 | # QtCreator Qml
39 | *.qmlproject.user
40 | *.qmlproject.user.*
41 |
42 | # QtCreator CMake
43 | CMakeLists.txt.user*
44 |
45 | .idea
46 |
47 | __pycache__
48 | *.egg-info
49 | dist
50 | build
51 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/app/app.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | SOURCES += main.cpp
4 |
5 | QDEP_LINK_DEPENDS += ../lib1 ../lib2
6 |
7 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
8 | !load(qdep):error("Failed to load qdep feature")
9 |
10 | !contains(DEFINES, LIB3_DEFINED): error("!LIB3_DEFINED")
11 | !contains(DEFINES, PROJECT4_DEFINED): error("!PROJECT4_DEFINED")
12 | !contains(QT, sql): error("sql")
13 |
14 | static {
15 | !contains(LIBS, -llib3): error("!-llib3")
16 | !contains(LIBS, -lproject4): error("!-lproject4")
17 | } else {
18 | contains(LIBS, -llib3): error("-llib3")
19 | contains(LIBS, -lproject4): error("-lproject4")
20 | }
21 |
22 | include(../../testrun.pri)
23 |
24 | message(LIBS: $$LIBS)
25 |
--------------------------------------------------------------------------------
/tests/project/tests.h:
--------------------------------------------------------------------------------
1 | #ifndef TESTS_H
2 | #define TESTS_H
3 |
4 | #include
5 |
6 | #define COMPARE_IMPL(x, y, file, line) do { \
7 | if((x) != (y)) { \
8 | qCritical().nospace() << file << ":" << line << ": COMPARE ERROR: " \
9 | << #x << "{" << (x) << "}" << " != " << #y << "{" << (y) << "}"; \
10 | return 1; \
11 | } \
12 | } while(false)
13 |
14 | #define COMPARE(x, y) COMPARE_IMPL(x, y, __FILE__, __LINE__)
15 |
16 | #define VERIFY_IMPL(x, file, line) do { \
17 | if(!(x)) { \
18 | qCritical().nospace() << file << ":" << line << ": VERIFY ERROR: !" << #x; \
19 | return 1; \
20 | } \
21 | } while(false);
22 |
23 | #define VERIFY(x) VERIFY_IMPL(x, __FILE__, __LINE__)
24 |
25 | #endif
26 |
--------------------------------------------------------------------------------
/tests/project/single/single.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | CONFIG += lrelease
4 |
5 | SOURCES += \
6 | main.cpp
7 |
8 | #CONFIG += qdep_no_qm_combine
9 |
10 | #REPO_BASE = file:///home/sky/Programming/QtLibraries/qdep/.git
11 | REPO_BASE = Skycoder42/qdep
12 |
13 | QDEP_DEPENDS += $${REPO_BASE}@master/tests/packages/basic/package1/package1.pri
14 |
15 | TRANSLATIONS += \
16 | single_de.ts \
17 | single_ja.ts
18 |
19 | force_ts|CONFIG(release, debug|release): DEFINES += WITH_TRANSLATIONS
20 |
21 | QM_FILES_INSTALL_PATH = $$[QT_INSTALL_TRANSLATIONS]
22 |
23 | !load(qdep):error("Failed to load qdep feature")
24 |
25 | DEFINES += "\"TS_DIR=\\\"$$__qdep_lrelease_real_dir\\\"\""
26 |
27 | include(../testrun.pri)
28 |
29 | !package1_included: error("!package1_included")
30 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/lib2/lib2.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | TARGET = lib2
4 |
5 | HEADERS += lib2.h
6 |
7 | SOURCES += lib2.cpp
8 |
9 | DEFINES += LIB2_BUILD
10 |
11 | QDEP_LINK_DEPENDS += ../lib3
12 |
13 | QDEP_PROJECT_ROOT = ..
14 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project4/project4.pro
15 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/qdep@master/tests/packages/mixdeps/project5/project5.pro
16 |
17 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
18 | !load(qdep):error("Failed to load qdep feature")
19 |
20 | !contains(DEFINES, LIB3_DEFINED): error("!LIB3_DEFINED")
21 | !contains(DEFINES, PROJECT4_DEFINED): error("!PROJECT4_DEFINED")
22 | !contains(QT, sql): error("sql")
23 |
24 | CONFIG += no_run_tests_target
25 | include(../../testrun.pri)
26 |
27 | message(LIBS: $$LIBS)
28 |
--------------------------------------------------------------------------------
/tests/project/basic/basic.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | SOURCES += \
4 | main.cpp
5 |
6 | #REPO_BASE = file:///home/sky/Programming/QtLibraries/qdep/.git
7 | REPO_BASE = Skycoder42/qdep
8 |
9 | QDEP_DEPENDS += $${REPO_BASE}@master/tests/packages/basic/package1/package1.pri
10 | QDEP_DEPENDS += $${REPO_BASE}@master/tests/packages/basic/package2/package2.pri
11 | QDEP_DEPENDS += $${REPO_BASE}@master/tests/packages/basic/package1/package1.pri
12 |
13 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
14 | !load(qdep):error("Failed to load qdep feature")
15 |
16 | include(../testrun.pri)
17 |
18 | !package1_included: error("!package1_included")
19 | !package2_included: error("!package2_included")
20 | !package3_included: error("!package3_included")
21 | !package4_included: error("!package4_included")
22 | !package5_included: error("!package5_included")
23 |
--------------------------------------------------------------------------------
/tests/project/external/libdynamic/libdynamic.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 |
3 | TARGET = dynamic
4 |
5 | DEFINES += LIBDYNAMIC_BUILD
6 |
7 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package4/package4.pri
8 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package5/package5.pri
9 |
10 | QDEP_HOOK_FNS += libdynamic_startup_hook
11 |
12 | QDEP_DEFINES += LIBDYNAMIC_TEST
13 | QDEP_INCLUDEPATH += $$PWD/extra
14 |
15 | HEADERS += libdynamic.h
16 |
17 | SOURCES += libdynamic.cpp
18 |
19 | #CONFIG += qdep_export
20 | CONFIG += qdep_export_all
21 |
22 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
23 | !load(qdep):error("Failed to load qdep feature")
24 |
25 | CONFIG += no_run_tests_target
26 | include(../../testrun.pri)
27 |
28 | !package4_included: error("!package4_included")
29 | !package5_included: error("!package5_included")
30 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import setuptools
2 | from qdep.qdep import version
3 |
4 | with open("README.md", "r") as fh:
5 | long_description = fh.read()
6 |
7 | setuptools.setup(
8 | name="qdep",
9 | version=version,
10 | author="Skycoder42",
11 | author_email="skycoder42.de@gmx.de",
12 | description="A very basic yet simple to use dependency management tool for qmake based projects",
13 | long_description=long_description,
14 | long_description_content_type="text/markdown",
15 | url="https://github.com/Skycoder42/qdep",
16 | packages=setuptools.find_packages(),
17 | install_requires=[
18 | "appdirs",
19 | "lockfile",
20 | "argcomplete"
21 | ],
22 | classifiers=[
23 | "Programming Language :: Python :: 3",
24 | "License :: OSI Approved :: BSD License",
25 | "Operating System :: OS Independent",
26 | ],
27 | entry_points={
28 | "console_scripts": [
29 | 'qdep=qdep.internal.cli:main'
30 | ]
31 | }
32 | )
33 |
--------------------------------------------------------------------------------
/tests/project/external/app/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 | #include
7 |
8 | #include
9 | #include
10 |
11 | namespace {
12 | bool startuped = false;
13 | }
14 |
15 | void app_startup_hook()
16 | {
17 | startuped = true;
18 | }
19 |
20 | int main(int argc, char **argv)
21 | {
22 | QCoreApplication app{argc, argv};
23 |
24 | VERIFY(StaticClass::startupRun());
25 | VERIFY(DynamicClass::startupRun());
26 | VERIFY(LibStatic::libStartupRun());
27 | VERIFY(LibDynamic::libStartupRun());
28 | VERIFY(startuped);
29 |
30 | LibStatic stat;
31 | COMPARE(stat.magicNumber(), 422);
32 | COMPARE(stat.magicFraction(), 0.42);
33 |
34 | LibDynamic dyna;
35 | COMPARE(dyna.magicNumber(), 425);
36 | COMPARE(dyna.magicFraction(), 0.24);
37 |
38 | COMPARE(dummy_sta(42), 42);
39 | COMPARE(dummy_dyn(42), 42);
40 |
41 | VERIFY(QFile::exists(":/package2.txt"));
42 | VERIFY(QFile::exists(":/static.txt"));
43 |
44 | return 0;
45 | }
46 |
--------------------------------------------------------------------------------
/tests/project/external/libstatic/libstatic.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = lib
2 | CONFIG += static
3 |
4 | TARGET = super-static
5 |
6 | debug_and_release:CONFIG(release, debug|release): DESTDIR = libout/release
7 | else:debug_and_release:CONFIG(debug, debug|release): DESTDIR = libout/debug
8 | else: DESTDIR = libout
9 |
10 | HEADERS += libstatic.h
11 |
12 | SOURCES += libstatic.cpp
13 |
14 | RESOURCES += \
15 | super-static.qrc
16 |
17 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package2/package2.pri
18 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package3/package3.pri
19 |
20 | QDEP_HOOK_FNS += libstatic::hooks::startup_hook
21 |
22 | QDEP_DEFINES += LIBSTATIC_TEST
23 | QDEP_INCLUDEPATH += $$PWD/extra
24 |
25 | QDEP_EXPORTS += Skycoder42/qdep@master/tests/packages/external/package2/package2.pri
26 |
27 | CONFIG += qdep_link_private
28 |
29 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
30 | !load(qdep):error("Failed to load qdep feature")
31 |
32 | CONFIG += no_run_tests_target
33 | include(../../testrun.pri)
34 |
35 | !package2_included: error("!package2_included")
36 | !package3_included: error("!package3_included")
37 |
--------------------------------------------------------------------------------
/tests/project/single/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | int main(int argc, char **argv)
9 | {
10 | QCoreApplication app{argc, argv};
11 | #ifdef WITH_TRANSLATIONS
12 | auto translator = new QTranslator{&app};
13 | auto ts_ok = translator->load(QLocale{QLocale::German, QLocale::Germany},
14 | QStringLiteral("single"),
15 | QStringLiteral("_"),
16 | QStringLiteral(TS_DIR));
17 | VERIFY(ts_ok);
18 | VERIFY(QCoreApplication::installTranslator(translator));
19 |
20 | Simple simple;
21 | simple.value = QCoreApplication::translate("GLOBAL", "Hello Tree");
22 | COMPARE(simple.transform(), QLatin1String("hallo baum"));
23 | COMPARE(simple.translate(), QLatin1String("Hallo Welt"));
24 | #else
25 | Simple simple;
26 | simple.value = QCoreApplication::translate("GLOBAL", "Hello Tree");
27 | COMPARE(simple.transform(), QLatin1String("hello tree"));
28 | COMPARE(simple.translate(), QLatin1String("Hello World"));
29 | #endif
30 | return 0;
31 | }
32 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 |
3 | services:
4 | - docker
5 |
6 | sudo: required
7 | dist: trusty
8 | osx_image: xcode10.2
9 |
10 | env:
11 | global:
12 | - QDEP_CACHE_DIR=$HOME/.qdep-cache
13 | - QT_VER=5.13.0
14 | - DOCKER_IMAGE=base
15 | - MAKE_RUN_TESTS=true
16 | - TARGET_NAME=qdep
17 |
18 | matrix:
19 | include:
20 | - os: linux
21 | env:
22 | - PLATFORM=gcc_64
23 | - os: linux
24 | env:
25 | - PLATFORM=android_arm64_v8a
26 | - os: linux
27 | env:
28 | - PLATFORM=android_x86_64
29 | - os: linux
30 | env:
31 | - PLATFORM=android_armv7
32 | - os: linux
33 | env:
34 | - PLATFORM=android_x86
35 | - os: osx
36 | env:
37 | - PLATFORM=clang_64
38 | - os: osx
39 | env:
40 | - PLATFORM=ios
41 |
42 | before_install:
43 | - git clone https://github.com/Skycoder42/QtModules.git ./qtmodules-travis
44 | - travis_wait 40 ./qtmodules-travis/ci/$TRAVIS_OS_NAME/setup.sh
45 |
46 | script:
47 | - ./tests/setup-travis.sh
48 | - ./qtmodules-travis/ci/$TRAVIS_OS_NAME/build.sh
49 |
50 | before_cache:
51 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
52 | - rm -rf $HOME/.gradle/caches/*/plugin-resolution/
53 | cache:
54 | directories:
55 | - $HOME/.gradle/caches/
56 | - $HOME/.gradle/wrapper/
57 | - $HOME/.android/build-cache
58 |
--------------------------------------------------------------------------------
/tests/project/external/app/app.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = app
2 |
3 | SOURCES += \
4 | main.cpp
5 |
6 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package1/package1.pri
7 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package3/package3.pri
8 | QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/external/package5/package5.pri
9 |
10 | QDEP_HOOK_FNS += app_startup_hook
11 |
12 | QDEP_LINK_DEPENDS += ../libstatic/libstatic.pro $$OUT_PWD/../libdynamic
13 | #CONFIG += qdep_no_link
14 |
15 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
16 | !load(qdep):error("Failed to load qdep feature")
17 |
18 | include(../../testrun.pri)
19 |
20 | !package1_included: error("!package1_included")
21 | package2_included: error("package2_included")
22 | package3_included: error("package3_included")
23 | package4_included: error("package4_included")
24 | package5_included: error("package5_included")
25 |
26 | !contains(DEFINES, PACKAGE1_DEFINED):error("!PACKAGE1_DEFINED")
27 | !contains(DEFINES, PACKAGE2_DEFINED):error("!PACKAGE2_DEFINED")
28 | contains(DEFINES, PACKAGE3_DEFINED):error("PACKAGE3_DEFINED")
29 | !contains(DEFINES, PACKAGE4_DEFINED):error("!PACKAGE4_DEFINED")
30 | !contains(DEFINES, PACKAGE5_DEFINED):error("!PACKAGE5_DEFINED")
31 |
32 | !contains(FUNNY_STUFF, baum42):error("QDEP_VAR_EXPORTS failed")
33 | !contains(FUNNY_STUFF, baum24):error("QDEP_VAR_EXPORTS failed")
34 |
--------------------------------------------------------------------------------
/tests/project/mixdeps/mixdeps.pro:
--------------------------------------------------------------------------------
1 | TEMPLATE = subdirs
2 |
3 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/mixdeps/project1/project1.pro
4 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/mixdeps/project3/project3.pro
5 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/mixdeps/project4/project4.pro
6 | QDEP_PROJECT_SUBDIRS += Skycoder42/qdep@master/tests/packages/mixdeps/project5/project5.pro
7 |
8 | SUBDIRS += lib1 lib2 lib3 app
9 |
10 | lib1.qdep_depends += Skycoder42/qdep@master/tests/packages/mixdeps/project1/project1.pro
11 | lib1.qdep_depends += Skycoder42/qdep@master/tests/packages/mixdeps/project3/project3.pro
12 | lib2.depends += lib3
13 | lib2.qdep_depends += Skycoder42/qdep@master/tests/packages/mixdeps/project4/project4.pro
14 | lib2.qdep_depends += Skycoder42/qdep@master/tests/packages/mixdeps/project5/project5.pro
15 | app.depends += lib1 lib2
16 |
17 | CONFIG += qdep_no_pull # disable for performance - still enabled in first test
18 | !load(qdep):error("Failed to load qdep feature")
19 |
20 | CONFIG += no_run_tests_target
21 | prepareRecursiveTarget(run-tests)
22 | include(../testrun.pri)
23 |
24 | message($$SUBDIRS)
25 |
26 | # Project dependency structure:
27 | # app
28 | # |--lib1(s)
29 | # | |--project1(s)
30 | # | | |--project2(s)
31 | # | |--project3(d)
32 | # |--lib2(d)
33 | # | |--lib3(s)
34 | # | |--project4(s)
35 | # | |--project5(d)
36 | # | | |--project3(d)
37 |
--------------------------------------------------------------------------------
/tests/testrun.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # $1 build path
3 | # $2 qmake path
4 | # $3 test commands only
5 | set -ex
6 |
7 | SCRIPT_PATH="$(readlink -f "$(dirname "$0")")"
8 | TEST_PATH="$1"
9 | BUILD_PATH="$1/project"
10 | CMD_PATH="$1/cmd"
11 | shift
12 | QMAKE="${1:-qmake}"
13 | shift || true
14 | COMMANDS="$1"
15 | shift || true
16 |
17 | mkdir -p "$TEST_PATH/testgitroot/Skycoder42"
18 | if [ ! -e "$TEST_PATH/testgitroot/Skycoder42/qdep" ]; then
19 | ln -s "$(realpath "$SCRIPT_PATH/..")" "$TEST_PATH/testgitroot/Skycoder42/qdep"
20 | fi
21 |
22 | export PYTHONPATH="$(realpath "$SCRIPT_PATH/.."):$PYTHONPATH"
23 | export QDEP_CACHE_DIR="$TEST_PATH/qdep-cache"
24 | export QDEP_DEFAULT_PKG_FN="$TEST_PATH/testgitroot/{}/.git"
25 |
26 | "$SCRIPT_PATH/testentry.py" prfgen --qmake "$QMAKE"
27 |
28 | if [ -z "$COMMANDS" ]; then
29 | rm -rf "$BUILD_PATH"
30 | mkdir -p "$BUILD_PATH"
31 | cd "$BUILD_PATH"
32 | "$QMAKE" "CONFIG+=local_test_run" "$SCRIPT_PATH/project/"
33 | make qmake_all
34 | make
35 |
36 | make INSTALL_ROOT="$BUILD_PATH/install" install
37 | [ -e "$BUILD_PATH/install$($QMAKE -query QT_INSTALL_TRANSLATIONS)/single_de.qm" ]
38 | [ -e "$BUILD_PATH/install$($QMAKE -query QT_INSTALL_TRANSLATIONS)/single_ja.qm" ]
39 |
40 | export LD_LIBRARY_PATH="$BUILD_PATH/external/libdynamic/:$LD_LIBRARY_PATH"
41 | make run-tests
42 | else
43 | mkdir -p "$CMD_PATH"
44 | cd "$CMD_PATH"
45 | expanded=$@
46 | "$QMAKE" "CONFIG+=local_test_run" "TEST_RUN_ARGS=$expanded" "$SCRIPT_PATH/project/commands"
47 | make run-tests
48 | fi
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2018, Felix Barz
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/tests/project/testrun.pri:
--------------------------------------------------------------------------------
1 | CONFIG += console
2 | CONFIG -= app_bundle
3 |
4 | HEADERS += $$PWD/tests.h
5 | INCLUDEPATH += $$PWD
6 |
7 | !no_run_tests_target:!isEmpty(OUT_PWD) {
8 | win32:!ReleaseBuild:!DebugBuild {
9 | runtarget.target = run-tests
10 | runtarget.CONFIG = recursive
11 | runtarget.recurse_target = run-tests
12 | QMAKE_EXTRA_TARGETS += runtarget
13 | } else {
14 | oneshell.target = .ONESHELL
15 | QMAKE_EXTRA_TARGETS += oneshell
16 |
17 | LIB_DIRS = $$OUT_PWD
18 | LIB_DIRS_SHELL = $$shell_path($$OUT_PWD)
19 | prefix = "-L"
20 | for(lib_arg, LIBS):equals(prefix, $$str_member($$lib_arg, 0, 1)) {
21 | lib_arg = $$str_member($$lib_arg, 2, -1)
22 | LIB_DIRS += $$lib_arg
23 | LIB_DIRS_SHELL += $$shell_path($$lib_arg)
24 | }
25 | message("Detected library paths as: $$LIB_DIRS")
26 | message("Detected shell library paths as: $$LIB_DIRS_SHELL")
27 |
28 | win32:!win32-g++ {
29 | CONFIG(debug, debug|release): outdir_helper = debug
30 | CONFIG(release, debug|release): outdir_helper = release
31 | runtarget.target = run-tests
32 | runtarget.depends += $(DESTDIR_TARGET)
33 | runtarget.commands += set PATH=$$join(LIB_DIRS_SHELL, ";");$$shell_path($$[QT_INSTALL_BINS]);$(PATH)
34 | runtarget.commands += $$escape_expand(\\n\\t)set QT_PLUGIN_PATH=$$shadowed($$dirname(_QMAKE_CONF_))/plugins;$(QT_PLUGIN_PATH)
35 | runtarget.commands += $$escape_expand(\\n\\t)set QML2_IMPORT_PATH=$$shadowed($$dirname(_QMAKE_CONF_))/qml;$(QML2_IMPORT_PATH)
36 | runtarget.commands += $$escape_expand(\\n\\t)if exist $${outdir_helper}\\fail del $${outdir_helper}\\fail
37 | runtarget.commands += $$escape_expand(\\n\\t)start /w call $(DESTDIR_TARGET) 2^> $${outdir_helper}\\test.log ^|^| echo FAIL ^> $${outdir_helper}\\fail ^& exit 0
38 | runtarget.commands += $$escape_expand(\\n\\t)type $${outdir_helper}\\test.log
39 | runtarget.commands += $$escape_expand(\\n\\t)if exist $${outdir_helper}\\fail exit 42
40 | QMAKE_EXTRA_TARGETS += runtarget
41 | } else {
42 | win32-g++: QMAKE_DIRLIST_SEP = ";"
43 | runtarget.commands += export PATH=\"$$join(LIB_DIRS_SHELL, ":"):$$shell_path($$[QT_INSTALL_BINS]):$${LITERAL_DOLLAR}$${LITERAL_DOLLAR}PATH\"
44 | runtarget.commands += $$escape_expand(\\n\\t)export QT_PLUGIN_PATH=\"$$shadowed($$dirname(_QMAKE_CONF_))/plugins/$${QMAKE_DIRLIST_SEP}$(QT_PLUGIN_PATH)\"
45 | runtarget.commands += $$escape_expand(\\n\\t)export QML2_IMPORT_PATH=\"$$shadowed($$dirname(_QMAKE_CONF_))/qml/$${QMAKE_DIRLIST_SEP}$(QML2_IMPORT_PATH)\"
46 | win32-g++: QMAKE_DIRLIST_SEP = ":"
47 |
48 | linux|win32-g++ {
49 | runtarget.commands += $$escape_expand(\\n\\t)export LD_LIBRARY_PATH=\"$$join(LIB_DIRS, "$${QMAKE_DIRLIST_SEP}$$")$${QMAKE_DIRLIST_SEP}$$[QT_INSTALL_LIBS]$${QMAKE_DIRLIST_SEP}$(LD_LIBRARY_PATH)\"
50 | runtarget.commands += $$escape_expand(\\n\\t)export QT_QPA_PLATFORM=minimal
51 | } else:mac {
52 | runtarget.commands += $$escape_expand(\\n\\t)export DYLD_LIBRARY_PATH=\"$$join(LIB_DIRS, ":"):$$[QT_INSTALL_LIBS]:$(DYLD_LIBRARY_PATH)\"
53 | runtarget.commands += $$escape_expand(\\n\\t)export DYLD_FRAMEWORK_PATH=\"$$join(LIB_DIRS, ":"):$$[QT_INSTALL_LIBS]:$(DYLD_FRAMEWORK_PATH)\"
54 | }
55 |
56 | runtarget.target = run-tests
57 | win32-g++ {
58 | runtarget.depends += $(DESTDIR_TARGET)
59 | runtarget.commands += $$escape_expand(\\n\\t)./$(DESTDIR_TARGET)
60 | } else {
61 | runtarget.depends += $(TARGET)
62 | runtarget.commands += $$escape_expand(\\n\\t)./$(TARGET)
63 | }
64 | QMAKE_EXTRA_TARGETS += runtarget
65 | }
66 | }
67 | } else: QMAKE_EXTRA_TARGETS += run-tests
68 |
69 | # qdep stats
70 | message("TARGET: $$TARGET")
71 | !qdep_build:error("qdep was loaded, but qdep_build config is not set")
72 | !isEmpty(__QDEP_REAL_DEPS_STACK):error("__QDEP_REAL_DEPS_STACK not empty: $$__QDEP_REAL_DEPS_STACK")
73 | message("__QDEP_INCLUDE_CACHE:")
74 | for(hash, __QDEP_INCLUDE_CACHE) {
75 | message(" $${hash}.package: $$eval($${hash}.package)")
76 | message(" $${hash}.version: $$eval($${hash}.version)")
77 | message(" $${hash}.path: $$eval($${hash}.path)")
78 | message(" $${hash}.exports: $$eval($${hash}.exports)")
79 | message(" $${hash}.local: $$eval($${hash}.local)")
80 | message(" $${hash}.target: $$eval($${hash}.target)")
81 | message(" $${hash}.file: $$eval($${hash}.file)")
82 | message(" $${hash}.depends: $$eval($${hash}.depends)")
83 | }
84 | message("QDEP_DEFINES: $$QDEP_DEFINES")
85 | message("DEFINES: $$DEFINES")
86 | message("QDEP_INCLUDEPATH: $$QDEP_INCLUDEPATH")
87 | message("INCLUDEPATH: $$INCLUDEPATH")
88 | message("__QDEP_PRIVATE_VARS_EXPORT: $$__QDEP_PRIVATE_VARS_EXPORT")
89 |
--------------------------------------------------------------------------------
/qdep/internal/private.py:
--------------------------------------------------------------------------------
1 | from qdep.internal.common import *
2 |
3 |
4 | def dephash(*packages, project=False, pkgpath=False):
5 | for package in packages:
6 | pkg_url, pkg_branch, pkg_path = package_resolve(package, project=project)
7 | if pkgpath:
8 | print(pkg_hash(pkg_url, pkg_path) + ";" + pkg_path)
9 | else:
10 | print(pkg_hash(pkg_url, pkg_path))
11 |
12 |
13 | def pkgresolve(package, latest_version=None, project=False, pull=True, clone=True):
14 | ov_map = get_override_map()
15 |
16 | pkg_url, pkg_branch, pkg_path = package_resolve(package, pkg_version=latest_version, project=project)
17 | needs_cache = pkg_branch is None
18 | if pkg_url in ov_map:
19 | pkg_base = ov_map[pkg_url]
20 | else:
21 | pkg_base, pkg_branch = get_sources(pkg_url, pkg_branch, pull=pull, clone=clone)
22 | print(pkg_hash(pkg_url, pkg_path))
23 | print(pkg_branch)
24 | print(pkg_base)
25 | print(pkg_path)
26 | print(needs_cache)
27 |
28 |
29 | def hookgen(prefix, header, resources=None, hooks=None):
30 | if resources is None:
31 | resources = []
32 | if hooks is None:
33 | hooks = []
34 |
35 | inc_guard = cpp_escape(path.basename(header)).upper()
36 | with open(header, "w") as out_file:
37 | out_file.write("#ifndef {}\n".format(inc_guard))
38 | out_file.write("#define {}\n\n".format(inc_guard))
39 |
40 | out_file.write("#include \n\n")
41 |
42 | for hook in hooks:
43 | out_file.write(declare_hook(hook))
44 |
45 | out_file.write("\ninline void qdep_{}_init() {{\n".format(cpp_escape(prefix)))
46 | out_file.write("\t// resources\n")
47 | for resource in resources: # ignore the first argument, as it is always the current pro file (to ensure this rule is always run
48 | out_file.write("\tQ_INIT_RESOURCE({});\n".format(cpp_escape(path.splitext(path.basename(resource))[0])))
49 | out_file.write("\t// hooks\n")
50 | for hook in hooks:
51 | out_file.write("\t::{}();\n".format(hook))
52 | out_file.write("}\n\n")
53 |
54 | out_file.write("#endif //{}\n".format(inc_guard))
55 |
56 |
57 | def hookimp(outfile, headers=None, hooks=None):
58 | if headers is None:
59 | headers = []
60 | if hooks is None:
61 | hooks = []
62 |
63 | with open(outfile, "w") as out_file:
64 | targets = []
65 | target_regex = re.compile(r".*qdep_(.*)_hooks\.h$")
66 |
67 | out_file.write("#include \n")
68 | for header in headers:
69 | abs_path = path.abspath(header)
70 | out_file.write("#include \"{}\"\n".format(abs_path))
71 | targets.append(re.match(target_regex, header).group(1))
72 |
73 | out_file.write("\n")
74 | for hook in hooks:
75 | out_file.write(declare_hook(hook))
76 |
77 | out_file.write("\nnamespace {\n\n")
78 | out_file.write("void __qdep_startup_hooks() {\n")
79 | for target in targets:
80 | out_file.write("\tqdep_{}_init();\n".format(cpp_escape(target)))
81 | for hook in hooks:
82 | out_file.write("\t::{}();\n".format(hook))
83 | out_file.write("}\n\n")
84 | out_file.write("}\n")
85 | out_file.write("Q_COREAPP_STARTUP_FUNCTION(__qdep_startup_hooks)\n")
86 |
87 |
88 | def lconvert(tsfile, outfile, *combine_files, lconvert_args=None):
89 | if lconvert_args is None:
90 | lconvert_args = ["lconvert"]
91 |
92 | # sort combine args into a map of the languages
93 | sub_map = dict()
94 | for sub_arg in combine_files:
95 | sub_base = path.splitext(path.basename(sub_arg))[0]
96 | sub_args = sub_base.split("_")[1:]
97 | for arg_cnt in range(len(sub_args)):
98 | suffix = "_".join(sub_args[arg_cnt:])
99 | if suffix not in sub_map:
100 | sub_map[suffix] = []
101 | sub_map[suffix].append(sub_arg)
102 |
103 | # find the qm files to combine with the input
104 | target_base = path.splitext(path.basename(tsfile))[0]
105 | ts_args = target_base.split("_")[1:]
106 | combine_list = []
107 | for arg_cnt in range(len(ts_args) - 1, -1, -1):
108 | suffix = "_".join(ts_args[arg_cnt:])
109 | if suffix in sub_map:
110 | combine_list = combine_list + sub_map[suffix]
111 | combine_list.append(tsfile)
112 |
113 | # run lconvert
114 | sub_run(lconvert_args + ["-if", "ts", "-i"] + combine_list + ["-of", "ts", "-o", outfile], check=True)
115 |
116 |
117 | def prolink(prodir, pkghash, pkgpath, link=None):
118 | link_target = path.join(prodir, ".qdep", pkghash, "src")
119 | pro_target = path.join(link_target, pkgpath[1:])
120 |
121 | # skip mode -> only print the path
122 | if link is None:
123 | print(pro_target)
124 | return
125 |
126 | # normal mode -> first unlink any existing stuff
127 | if path.islink(link_target):
128 | if os.name == 'nt' and path.isdir(link_target):
129 | os.rmdir(link_target)
130 | else:
131 | os.unlink(link_target)
132 | elif path.isdir(link_target):
133 | shutil.rmtree(link_target)
134 | elif path.exists(link_target):
135 | os.remove(link_target)
136 |
137 | # then link the correct project again
138 | try:
139 | os.makedirs(path.dirname(link_target), exist_ok=True)
140 | os.symlink(link, link_target, target_is_directory=True)
141 | except OSError:
142 | # symlink is not possible, copy instead
143 | print("Project WARNING: Failed to symlink project dependecy. Performing deep copy instead", file=sys.stderr)
144 | shutil.copytree(link, link_target, symlinks=True, ignore_dangling_symlinks=True)
145 |
146 | print(pro_target)
147 |
--------------------------------------------------------------------------------
/qdep/qdep.py:
--------------------------------------------------------------------------------
1 | from qdep.internal.common import *
2 | from qdep.internal.prf import *
3 |
4 |
5 | version = "1.1.1"
6 |
7 |
8 | def prfgen(script_path, qmake="qmake", data_dir=None):
9 | if data_dir is not None:
10 | prf_path = data_dir
11 | else:
12 | qmake_res = sub_run([qmake, "-query", "QT_HOST_DATA"], check=True, stdout=subprocess.PIPE, encoding="UTF-8")
13 | prf_path = str(qmake_res.stdout).strip()
14 | prf_path = path.join(prf_path, "mkspecs", "features", "qdep.prf")
15 | print("Generating PRF-File as: ", prf_path)
16 | os.makedirs(path.dirname(prf_path), exist_ok=True)
17 | with open(prf_path, "w") as prf_file:
18 | prf_file.write("# Generated by {}\n".format(script_path))
19 | prf_file.write("qdep_force|!isEmpty(OUT_PWD) { # Used to detect if qdep was loaded from an indirect evaluation\n\n")
20 | prf_file.write("isEmpty(QDEP_PATH) {\n")
21 | prf_file.write("\tQDEP_PATH = {}\n".format(script_path))
22 | prf_file.write("\twin32: QDEP_PATH = $${QDEP_PATH}.exe\n")
23 | prf_file.write("}\n")
24 | prf_file.write("isEmpty(QDEP_VERSION): QDEP_VERSION = {}\n".format(version))
25 | prf_file.write(qdep_prf)
26 | prf_file.write("\n}\n")
27 |
28 |
29 | def init(profile):
30 | with open(profile, "a") as pro_file:
31 | pro_file.write("\nQDEP_DEPENDS += \n\n")
32 | pro_file.write("!load(qdep):error(\"Failed to load qdep feature! Run 'qdep prfgen --qmake $$QMAKE_QMAKE' to create it.\")\n")
33 |
34 |
35 | def lupdate(pri_path, qmake="qmake", lupdate_args=None):
36 | if lupdate_args is None:
37 | lupdate_args = []
38 |
39 | qmake_res = sub_run([qmake, "-query", "QT_HOST_BINS"], check=True, stdout=subprocess.PIPE, encoding="UTF-8")
40 | lupdate_path = path.join(str(qmake_res.stdout).strip(), "lupdate")
41 | with tempfile.TemporaryDirectory() as tmp_dir:
42 | tmp_pro_path = path.join(tmp_dir, "lupdate.pro")
43 | with open(tmp_pro_path, "w") as tmp_file:
44 | tmp_file.write("include({})\n".format(pri_path))
45 | tmp_file.write("TRANSLATIONS = $$QDEP_TRANSLATIONS\n")
46 | sub_run([lupdate_path] + lupdate_args + [tmp_pro_path], check=True)
47 |
48 |
49 | def clear(no_confirm=False):
50 | def folder_size(path):
51 | total = 0
52 | for entry in os.scandir(path):
53 | if entry.is_file():
54 | total += entry.stat().st_size
55 | elif entry.is_dir():
56 | total += folder_size(entry.path)
57 | return total
58 |
59 | if not no_confirm:
60 | print("All caches sources will be removed and have to be downloaded again. Make sure no other qdep instance is currently running!")
61 | ok = input("Do you really want ro remove all cached sources? [y/N]").lower()
62 | if len(ok) == 0:
63 | return
64 | elif ok != "y" and ok != "yes":
65 | return
66 |
67 | cache_dir = os.getenv("QDEP_CACHE_DIR", get_cache_dir_default())
68 | cache_dir = path.join(cache_dir, "src")
69 | if path.exists(cache_dir):
70 | rm_size = folder_size(cache_dir)
71 | shutil.rmtree(cache_dir)
72 | else:
73 | rm_size = 0
74 | print("Removed {} bytes".format(rm_size))
75 |
76 |
77 | def versions(package, tags=True, branches=False, short=False, limit=None):
78 | package_url, _v, _p = package_resolve(package)
79 |
80 | if tags:
81 | pkg_tags = get_all_tags(package_url, tags=True, branches=False, allow_empty=True)
82 | if limit is not None:
83 | pkg_tags = pkg_tags[-limit:]
84 | else:
85 | pkg_tags = []
86 |
87 | if branches:
88 | pkg_branches = get_all_tags(package_url, tags=False, branches=True, allow_empty=True)
89 | if limit is not None:
90 | pkg_branches = pkg_branches[-limit:]
91 | else:
92 | pkg_branches = []
93 |
94 | if short:
95 | print(" ".join(pkg_branches + pkg_tags))
96 | else:
97 | if branches:
98 | print("Branches:")
99 | if len(pkg_branches) > 0:
100 | for branch in pkg_branches:
101 | print(" " + branch)
102 | else:
103 | print(" -- No branches found --")
104 |
105 | if tags:
106 | if branches:
107 | print("")
108 |
109 | print("Tags:")
110 | if len(pkg_tags) > 0:
111 | for tag in pkg_tags:
112 | print(" " + tag)
113 | else:
114 | print(" -- No tags found --")
115 |
116 |
117 | def query(package, check=True, print_versions=False, expand=False):
118 | pkg_url, pkg_version, pkg_path = package_resolve(package)
119 | if check:
120 | if pkg_version is None:
121 | pkg_version = get_latest_tag(pkg_url, allow_empty=True, allow_error=True)
122 | pkg_exists = pkg_version is not None
123 | else:
124 | pkg_exists = pkg_version in get_all_tags(pkg_url, branches=True, tags=True, allow_empty=True, allow_error=True)
125 | else:
126 | pkg_exists = False # not knowing means false
127 |
128 | if expand:
129 | if pkg_version is None:
130 | raise Exception("Unable to determine package version")
131 | else:
132 | print("{}@{}{}".format(pkg_url, pkg_version, pkg_path))
133 | else:
134 | print("Input:", package)
135 | if pkg_version is not None:
136 | print("Expanded Name: {}@{}{}".format(pkg_url, pkg_version, pkg_path))
137 | else:
138 | print("Expanded Name:", None)
139 | print("URL:", pkg_url)
140 | print("Version:", pkg_version)
141 | print("Path:", pkg_path)
142 | if check:
143 | print("Exists:", pkg_exists)
144 |
145 | if print_versions:
146 | print("")
147 | versions(package, branches=True)
148 |
149 |
150 | def get(*targets, extract=False, evaluate=False, recurse=True, qmake="qmake", make="make", cache_dir=None):
151 | if cache_dir is not None:
152 | os.environ["QDEP_CACHE_DIR"] = cache_dir
153 |
154 | # eval the pro files if needed
155 | if evaluate:
156 | # special case: since we only download sources, a simple qmake run is enough, no actual "evaluation" needed
157 | for pro_file in targets:
158 | eval_pro_depends(pro_file, qmake, make)
159 | print("Done! qmake finished successfully, all qdep sources for the project tree have been downloaded.")
160 | return
161 | elif extract:
162 | packages = []
163 | for pro_file in targets:
164 | print("Extracting dependencies from {}...".format(pro_file))
165 | packages = packages + extract_pro_depends(pro_file, qmake)
166 | else:
167 | packages = list(targets)
168 |
169 | # download the actual sources
170 | print("Found {} initial packages".format(len(packages)))
171 | pkg_hashes = set()
172 | for package in packages:
173 | pkg_url, pkg_version, pkg_path = package_resolve(package)
174 | p_hash = pkg_hash(pkg_url, pkg_path)
175 | if p_hash in pkg_hashes:
176 | continue
177 |
178 | print("Downloading sources for {}...".format(package))
179 | cache_dir, _b = get_sources(pkg_url, pkg_version)
180 |
181 | if recurse:
182 | print("Extracting dependencies from {}...".format(package))
183 | new_packages = extract_pro_depends(path.join(cache_dir, pkg_path[1:]), qmake)
184 | print("Found {} dependent packages".format(len(new_packages)))
185 | for pkg in new_packages:
186 | packages.append(pkg)
187 |
188 | pkg_hashes.add(p_hash)
189 | print("Done!")
190 |
191 |
192 | def update(pro_file, evaluate=False, replace=False, qmake="qmake", make="make"):
193 | if evaluate:
194 | all_deps = invert_map(eval_pro_depends(pro_file, qmake, make, dump_depends=True))
195 | pkg_all, pkg_new = check_for_updates(all_deps.keys())
196 | update_files = set()
197 | for old_pkg, new_pkg in pkg_new.items():
198 | for dep in all_deps[old_pkg]:
199 | update_files.add(dep)
200 | all_deps[new_pkg] = (all_deps[new_pkg] if new_pkg in all_deps else []) + all_deps[old_pkg]
201 | all_deps.pop(old_pkg)
202 | all_deps = invert_map(all_deps)
203 | for upd_file in update_files:
204 | replace_or_print_update(upd_file, all_deps[upd_file], pkg_new, replace)
205 | else:
206 | print("Extracting dependencies from {}...".format(pro_file))
207 | packages = extract_pro_depends(pro_file, qmake)
208 | pkg_all, pkg_new = check_for_updates(packages)
209 | replace_or_print_update(pro_file, pkg_all, pkg_new, replace)
210 |
--------------------------------------------------------------------------------
/qdep/internal/common.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 | import os
3 | import re
4 | import shutil
5 | import stat
6 | import subprocess
7 | import sys
8 | import tempfile
9 | from os import path
10 | from collections import OrderedDict
11 |
12 | import appdirs
13 | from lockfile import LockFile
14 |
15 |
16 | def get_cache_dir_default():
17 | return appdirs.user_cache_dir("qdep")
18 |
19 |
20 | def get_cache_dir(pkg_url, pkg_branch):
21 | cache_dir = os.getenv("QDEP_CACHE_DIR", get_cache_dir_default())
22 | cache_dir = path.join(cache_dir, hashlib.sha3_256(pkg_url.encode("UTF-8")).hexdigest(), pkg_branch)
23 | os.makedirs(cache_dir, exist_ok=True)
24 | return cache_dir
25 |
26 |
27 | def get_override_map():
28 | or_env = os.getenv("QDEP_SOURCE_OVERRIDE")
29 | if or_env is None:
30 | return {}
31 |
32 | or_map = {}
33 | for env_info in or_env.split(";"):
34 | env_pair = env_info.split("^")
35 | if len(env_pair) == 2:
36 | or_map[env_pair[0]] = env_pair[1]
37 | return or_map
38 |
39 |
40 | def pkg_hash(pkg_url, pkg_path):
41 | return "__QDEP_PKG_" + hashlib.sha3_256((pkg_url + pkg_path).lower().encode("UTF-8")).hexdigest()
42 |
43 |
44 | def cpp_escape(name, ascii=True):
45 | return re.sub(r"[^\w]", "_", name, re.ASCII if ascii else re.UNICODE)
46 |
47 |
48 | def declare_hook(hook):
49 | last_sep = hook.rfind("::")
50 | if last_sep != -1:
51 | return "namespace {} {{ void {}(); }}\n".format(hook[:last_sep], hook[last_sep + 2:])
52 | else:
53 | return "void {}();\n".format(hook)
54 |
55 |
56 | def sub_run(*args, **kwargs):
57 | sys.stdout.flush()
58 | sys.stderr.flush()
59 | return subprocess.run(*args, **kwargs)
60 |
61 |
62 | def package_resolve(package, pkg_version=None, project=False, expand=True):
63 | pattern = re.compile(r'^(?:([^@\/]+\/[^@\/]+)|(\w+:\/\/.*\.git|[^@:]*@[^@]*:[^@]+\.git))(?:@([^\/\s]+)(\/.*)?)?$')
64 | path_pattern = re.compile(r'^.*\/([^\/]+)\/?\.git$')
65 |
66 | match = re.match(pattern, package)
67 | if not match:
68 | raise Exception("Given package is not a valid package descriptor: " + package)
69 |
70 | # extract package url
71 | if match.group(1) is not None:
72 | if not expand:
73 | pkg_url = match.group(1)
74 | else:
75 | pkg_url = os.getenv("QDEP_DEFAULT_PKG_FN", "https://github.com/{}.git").format(match.group(1))
76 | elif match.group(2) is not None:
77 | pkg_url = match.group(2)
78 | else:
79 | raise Exception("Invalid package specified: " + package)
80 |
81 | # extract branch
82 | if match.group(3) is not None:
83 | pkg_branch = match.group(3)
84 | elif pkg_version is not None:
85 | pkg_branch = pkg_version
86 | else:
87 | pkg_branch = None
88 |
89 | # extract pri path
90 | if match.group(4) is not None:
91 | pkg_path = match.group(4)
92 | elif not expand:
93 | pkg_path = ""
94 | else:
95 | pkg_path = "/" + re.match(path_pattern, pkg_url).group(1).lower() + (".pro" if project else ".pri")
96 |
97 | return pkg_url, pkg_branch, pkg_path
98 |
99 |
100 | def get_all_tags(pkg_url, branches=False, tags=True, allow_empty=False, allow_error=False):
101 | ls_args = ["git", "ls-remote", "--refs"]
102 | if not branches and tags:
103 | ls_args.append("--tags")
104 | elif not tags and branches:
105 | ls_args.append("--heads")
106 | if not allow_empty:
107 | ls_args.append("--exit-code")
108 | elif not tags and not branches:
109 | return [] # Nothing to check for
110 | ls_args.append(pkg_url)
111 |
112 | ls_res = sub_run(ls_args, check=not allow_error, stdout=subprocess.PIPE, encoding="UTF-8")
113 | ref_pattern = re.compile(r'[a-fA-F0-9]+\s+refs\/(?:tags|heads)\/([^\s]+)')
114 | tags = []
115 | for match in re.finditer(ref_pattern, ls_res.stdout):
116 | tags.append(match.group(1))
117 | return tags
118 |
119 |
120 | def get_latest_tag(pkg_url, allow_empty=False, allow_error=False):
121 | all_tags = get_all_tags(pkg_url, allow_empty=allow_empty, allow_error=allow_error)
122 | if len(all_tags) > 0:
123 | return all_tags[-1]
124 | else:
125 | return None
126 |
127 |
128 | def get_sources(pkg_url, pkg_branch, pull=True, clone=True):
129 |
130 | if pkg_branch is None:
131 | pkg_branch = get_latest_tag(pkg_url)
132 |
133 | cache_dir = get_cache_dir(pkg_url, pkg_branch)
134 |
135 | locker = LockFile(cache_dir)
136 | locker.acquire()
137 | try:
138 | needs_ro = False
139 | if path.isdir(path.join(cache_dir, ".git")):
140 | if pull and not path.exists(path.join(cache_dir, ".qdep_static_branch")):
141 | sub_run(["git", "pull", "--force", "--ff-only", "--update-shallow", "--recurse-submodules"], cwd=cache_dir, stdout=subprocess.DEVNULL, check=True)
142 | needs_ro = True
143 | elif clone:
144 | sub_run(["git", "clone", "--recurse-submodules", "--depth", "1", "--branch", pkg_branch, pkg_url, cache_dir], check=True)
145 | head_ref_res = sub_run(["git", "symbolic-ref", "HEAD"], cwd=cache_dir, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
146 | if head_ref_res.returncode != 0:
147 | open(path.join(cache_dir, ".qdep_static_branch"), 'a').close()
148 | needs_ro = True
149 | else:
150 | raise Exception("The --no-clone flag was specified - cannot install new packages with pulling disabled")
151 |
152 | if needs_ro:
153 | NO_WRITE_MASK = ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH
154 | for root, dirs, files in os.walk(cache_dir, topdown=True):
155 | dirs[:] = [d for d in dirs if d != ".git"]
156 | for file in files:
157 | f_path = path.join(root, file)
158 | cur_perm = stat.S_IMODE(os.lstat(f_path).st_mode)
159 | os.chmod(f_path, cur_perm & NO_WRITE_MASK)
160 | except:
161 | shutil.rmtree(cache_dir, ignore_errors=True)
162 | raise
163 | finally:
164 | locker.release()
165 |
166 | return cache_dir, pkg_branch
167 |
168 |
169 | def extract_pro_depends(pro_file, qmake):
170 | packages = []
171 |
172 | with tempfile.TemporaryDirectory() as tmp_dir:
173 | dump_name = path.join(tmp_dir, "qdep_dummy.pro")
174 | with open(dump_name, "w") as dump_file:
175 | dump_file.write("depends += $$fromfile($$quote({}), QDEP_DEPENDS)\n".format(pro_file))
176 | dump_file.write("depends += $$fromfile($$quote({}), QDEP_PROJECT_SUBDIRS)\n".format(pro_file))
177 | dump_file.write("depends += $$fromfile($$quote({}), QDEP_PROJECT_LINK_DEPENDS)\n".format(pro_file))
178 | dump_file.write("depends += $$fromfile($$quote({}), QDEP_PROJECT_DEPENDS)\n".format(pro_file))
179 | dump_file.write("!write_file($$PWD/qdep_depends.txt, depends):error(\"write error\")\n")
180 | sub_run([qmake, dump_name], cwd=tmp_dir, check=True, stdout=subprocess.DEVNULL)
181 | with open(path.join(tmp_dir, "qdep_depends.txt"), "r") as dep_file:
182 | for line in dep_file.readlines():
183 | packages.append(line.strip())
184 |
185 | return packages
186 |
187 |
188 | def eval_pro_depends(pro_file, qmake, make, dump_depends=False):
189 | with tempfile.TemporaryDirectory() as tmp_dir:
190 | print("Running {} on {}...".format(qmake, pro_file))
191 | sub_run([qmake] + (["CONFIG+=__qdep_dump_dependencies"] if dump_depends else []) + [pro_file], cwd=tmp_dir, check=True, stdout=subprocess.DEVNULL)
192 | print("Running {} qmake_all...".format(make))
193 | sub_run([make, "qmake_all"], cwd=tmp_dir, check=True, stdout=subprocess.DEVNULL)
194 |
195 | if dump_depends:
196 | all_deps = {}
197 | for root, dirs, files in os.walk(tmp_dir, topdown=True):
198 | for file in files:
199 | if file == "qdep_depends.txt":
200 | with open(path.join(root, file), "r") as dep_list:
201 | dep_name = dep_list.readline().strip()
202 | all_deps[dep_name] = []
203 | for dep in dep_list:
204 | all_deps[dep_name].append(dep.strip())
205 |
206 | return all_deps
207 | else:
208 | return None
209 |
210 |
211 | def invert_map(original):
212 | inverted = OrderedDict()
213 | for key, values in original.items():
214 | for value in values:
215 | if value not in inverted:
216 | inverted[value] = [key]
217 | else:
218 | inverted[value].append(key)
219 | return inverted
220 |
221 |
222 | def check_for_updates(packages):
223 | pkg_all = []
224 | pkg_new = {}
225 | for package in packages:
226 | pkg_url, pkg_version, _p = package_resolve(package)
227 | if pkg_version is None:
228 | pkg_all.append(package)
229 | continue
230 |
231 | # check if the package actually has any tags
232 | all_tags = get_all_tags(pkg_url, allow_empty=True)
233 | if len(all_tags) == 0:
234 | pkg_all.append(package)
235 | continue
236 |
237 | # check if actually a tag and not a branch
238 | if pkg_version not in all_tags and pkg_version in get_all_tags(pkg_url, branches=True, tags=False, allow_empty=True):
239 | pkg_all.append(package)
240 | continue
241 |
242 | # check if latest tag has changed
243 | if all_tags[-1] != pkg_version:
244 | pkg_name, _v, pkg_path = package_resolve(package, expand=False)
245 | print("Found a new version for package {}: {} -> {}".format(pkg_name, pkg_version, all_tags[-1]))
246 | new_pkg = "{}@{}{}".format(pkg_name, all_tags[-1], pkg_path)
247 | pkg_all.append(new_pkg)
248 | pkg_new[package] = new_pkg
249 | else:
250 | pkg_all.append(package)
251 |
252 | return pkg_all, pkg_new
253 |
254 |
255 | def replace_or_print_update(pro_file, pkg_all, pkg_new, replace):
256 | if replace:
257 | with open(pro_file, "r") as in_file:
258 | with open(pro_file + ".qdepnew", "w") as out_file:
259 | for line in in_file:
260 | for src, repl in pkg_new.items():
261 | line = line.replace(src, repl)
262 | out_file.write(line)
263 |
264 | os.remove(pro_file)
265 | os.rename(pro_file + ".qdepnew", pro_file)
266 | print("Updated dependencies in {} - please check the file to make sure it has not been corrupted!".format(pro_file))
267 | else:
268 | print("")
269 | print("Dependencies for {}:".format(pro_file))
270 | print("QDEP_DEPENDS =", format(" \\\n\t".join(pkg_all)))
271 |
--------------------------------------------------------------------------------
/tests/project/commands/test-commands.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import sys
4 | import os
5 | import shutil
6 | import subprocess
7 | import xml.etree.ElementTree as ET
8 |
9 |
10 | is_local_run = False
11 | tests_allowed = None
12 | qdep_path = None
13 | qmake_path = None
14 | make_path = None
15 |
16 |
17 | def exec_process(name, proc, args, keep_stdout=False, keep_stderr=False):
18 | print("Executing:", name, " ".join(args))
19 |
20 | if "QDEP_DEFAULT_PKG_FN" in os.environ:
21 | proc_env = os.environ.copy()
22 | proc_env.pop("QDEP_DEFAULT_PKG_FN")
23 | else:
24 | proc_env = os.environ
25 |
26 | sys.stdout.flush()
27 | run_res = subprocess.run([proc] + list(args), cwd=os.getcwd(), check=True, stdout=subprocess.PIPE if keep_stdout else None, stderr=subprocess.PIPE if keep_stderr else None, encoding="UTF-8", env=proc_env)
28 |
29 | if keep_stdout:
30 | stdout_data = str(run_res.stdout)
31 | else:
32 | stdout_data = None
33 | if keep_stderr:
34 | stderr_data = str(run_res.stderr)
35 | else:
36 | stderr_data = None
37 | return stdout_data, stderr_data
38 |
39 |
40 | def exec_qdep(*args, keep_stdout=False, keep_stderr=False, trace=True):
41 | return exec_process("qdep", qdep_path, (["--trace"] if trace else []) + list(args), keep_stdout=keep_stdout, keep_stderr=keep_stderr)
42 |
43 |
44 | def exec_qmake(*args, keep_stdout=False, keep_stderr=False):
45 | return exec_process("qmake", qmake_path, args, keep_stdout=keep_stdout, keep_stderr=keep_stderr)
46 |
47 |
48 | def test_prfgen():
49 | exec_qdep("prfgen", "-d", os.getcwd())
50 | assert os.path.isfile("mkspecs/features/qdep.prf")
51 | with open("mkspecs/features/qdep.prf", "r") as prf_in:
52 | print("qdep.prf: ", prf_in.readline().strip())
53 |
54 |
55 | def test_init():
56 | with open("test.pro", "w") as pro_file:
57 | pro_file.write("TEMPLATE = aux\n")
58 | exec_qdep("init", "test.pro")
59 | with open("test.pro", "a") as pro_file:
60 | pro_file.write("!qdep_build:error(\"qdep loaded but not activated!\")\n")
61 | exec_qmake()
62 |
63 |
64 | def test_lupdate():
65 | with open("test.pri", "w") as pri_file:
66 | pri_file.write("SOURCES += $$PWD/test.cpp\n")
67 | pri_file.write("QDEP_TRANSLATIONS += $$PWD/test_de.ts\n")
68 | with open("test.cpp", "w") as cpp_file:
69 | cpp_file.write("#include \n\n")
70 | cpp_file.write("void test() {\n")
71 | cpp_file.write("\tauto x = QCoreApplication::translate(\"GLOBAL\", \"Hello Tree\");\n")
72 | cpp_file.write("}\n")
73 | exec_qdep("lupdate", "--qmake", qmake_path, "--pri-file", os.path.abspath("test.pri"), "--", "-no-ui-lines")
74 |
75 | assert os.path.isfile("test.pri")
76 | root = ET.parse("test_de.ts").getroot()
77 | assert root[0][1][1].text == "Hello Tree"
78 |
79 |
80 | def test_versions():
81 | def v_fetch(*args, strip=True):
82 | vs_sout, _e = exec_qdep("versions", *args, "Skycoder42/qpmx-sample-package", keep_stdout=True)
83 | if strip:
84 | vs_list = map(lambda s: s.strip(), vs_sout.strip().split("\n"))
85 | return list(vs_list)
86 | else:
87 | return vs_sout.strip().split(" ")
88 |
89 | v_res = v_fetch()
90 | assert v_res[0:5] == ["Tags:", "1.0.0", "1.0.1", "1.0.10", "1.0.11"]
91 | assert v_res[-1] == "1.2.0"
92 |
93 | v_res = v_fetch("-b", "--no-tags")
94 | assert v_res == ["Branches:", "master"]
95 |
96 | v_res = v_fetch("-b")
97 | assert v_res[0:8] == ["Branches:", "master", "", "Tags:", "1.0.0", "1.0.1", "1.0.10", "1.0.11"]
98 | assert v_res[-1] == "1.2.0"
99 |
100 | v_res = v_fetch("-b", "-s", strip=False)
101 | assert v_res[0:5] == ["master", "1.0.0", "1.0.1", "1.0.10", "1.0.11"]
102 | assert v_res[-1] == "1.2.0"
103 |
104 | v_res = v_fetch("-s", "--limit", "5", strip=False)
105 | assert v_res == ["1.0.7", "1.0.8", "1.0.9", "1.1.0", "1.2.0"]
106 |
107 |
108 | def test_query():
109 | def q_fetch(*args, package="Skycoder42/qpmx-sample-package", keep_stderr=False):
110 | qry_sout, _e = exec_qdep("query", *args, package, keep_stdout=True, keep_stderr=keep_stderr)
111 | qry_list = map(lambda s: s.strip(), qry_sout.strip().split("\n"))
112 | return list(qry_list)
113 |
114 | q_res = q_fetch()
115 | assert q_res == [
116 | "Input: Skycoder42/qpmx-sample-package",
117 | "Expanded Name: https://github.com/Skycoder42/qpmx-sample-package.git@1.2.0/qpmx-sample-package.pri",
118 | "URL: https://github.com/Skycoder42/qpmx-sample-package.git",
119 | "Version: 1.2.0",
120 | "Path: /qpmx-sample-package.pri",
121 | "Exists: True"
122 | ]
123 |
124 | q_res = q_fetch("--expand")
125 | assert q_res == ["https://github.com/Skycoder42/qpmx-sample-package.git@1.2.0/qpmx-sample-package.pri"]
126 |
127 | q_res = q_fetch("--no-check")
128 | assert q_res == [
129 | "Input: Skycoder42/qpmx-sample-package",
130 | "Expanded Name: None",
131 | "URL: https://github.com/Skycoder42/qpmx-sample-package.git",
132 | "Version: None",
133 | "Path: /qpmx-sample-package.pri"
134 | ]
135 |
136 | q_res = q_fetch("--no-check", package="Skycoder42/qpmx-sample-package@1.2.0")
137 | assert q_res == [
138 | "Input: Skycoder42/qpmx-sample-package@1.2.0",
139 | "Expanded Name: https://github.com/Skycoder42/qpmx-sample-package.git@1.2.0/qpmx-sample-package.pri",
140 | "URL: https://github.com/Skycoder42/qpmx-sample-package.git",
141 | "Version: 1.2.0",
142 | "Path: /qpmx-sample-package.pri"
143 | ]
144 |
145 | q_res = q_fetch("--versions")
146 | assert q_res[0:15] == [
147 | "Input: Skycoder42/qpmx-sample-package",
148 | "Expanded Name: https://github.com/Skycoder42/qpmx-sample-package.git@1.2.0/qpmx-sample-package.pri",
149 | "URL: https://github.com/Skycoder42/qpmx-sample-package.git",
150 | "Version: 1.2.0",
151 | "Path: /qpmx-sample-package.pri",
152 | "Exists: True",
153 | "",
154 | "Branches:", "master", "", "Tags:", "1.0.0", "1.0.1", "1.0.10", "1.0.11"
155 | ]
156 |
157 | q_res = q_fetch(package="Skycoder42/qpmx-sample-package@9.9.9")
158 | assert q_res == [
159 | "Input: Skycoder42/qpmx-sample-package@9.9.9",
160 | "Expanded Name: https://github.com/Skycoder42/qpmx-sample-package.git@9.9.9/qpmx-sample-package.pri",
161 | "URL: https://github.com/Skycoder42/qpmx-sample-package.git",
162 | "Version: 9.9.9",
163 | "Path: /qpmx-sample-package.pri",
164 | "Exists: False"
165 | ]
166 |
167 | q_res = q_fetch(package="file:///invalid/path/.git", keep_stderr=True)
168 | assert q_res == [
169 | "Input: file:///invalid/path/.git",
170 | "Expanded Name: None",
171 | "URL: file:///invalid/path/.git",
172 | "Version: None",
173 | "Path: /path.pri",
174 | "Exists: False"
175 | ]
176 |
177 |
178 | def test_get():
179 | def g_fetch(*args):
180 | sout, serr = exec_qdep("get", "--qmake", qmake_path, "--make", make_path, "--dir", os.getcwd(), *args, keep_stdout=True, keep_stderr=True)
181 |
182 | sout = sout.strip()
183 | if len(sout) == 0:
184 | sout = []
185 | else:
186 | sout = list(map(lambda s: s.strip(), sout.strip().split("\n")))
187 |
188 | serr = serr.strip()
189 | if len(serr) == 0:
190 | serr = []
191 | else:
192 | serr = list(map(lambda s: s.strip(), serr.strip().split("\n")))
193 |
194 | return sout, serr
195 |
196 | sout, serr = g_fetch("Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri")
197 | assert len(sout) == 5
198 | assert len(serr) == 1
199 |
200 | sout, serr = g_fetch("--no-recurse", "Skycoder42/qdep@master/tests/packages/basic/package2/package2.pri")
201 | assert len(sout) == 3
202 | assert len(serr) == 0
203 |
204 | sout, serr = g_fetch("Skycoder42/qdep@master/tests/packages/basic/package2/package2.pri")
205 | assert len(sout) == 14
206 | assert len(serr) == 0
207 |
208 | with open("extract.pro", "w") as pro_file:
209 | pro_file.write("QDEP_DEPENDS = Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n\n")
210 | exec_qdep("init", os.path.abspath("extract.pro"))
211 | sout, serr = g_fetch("--extract", os.path.abspath("extract.pro"))
212 | assert len(sout) == 6
213 | assert len(serr) == 0
214 |
215 | with open("eval.pro", "w") as pro_file:
216 | pro_file.write("TEMPLATE = subdirs\n")
217 | pro_file.write("SUBDIRS += sub_eval\n")
218 | os.mkdir("sub_eval")
219 | with open("sub_eval/sub_eval.pro", "w") as pro_file:
220 | pro_file.write("TEMPLATE = aux\n")
221 | pro_file.write("QDEP_DEPENDS = Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n\n")
222 | exec_qdep("init", os.path.abspath("sub_eval/sub_eval.pro"))
223 | sout, serr = g_fetch("--eval", os.path.abspath("eval.pro"))
224 | print("qmake/make error output:\n\t", "\n\t".join(serr))
225 | assert len(sout) == 3
226 |
227 |
228 | def test_update():
229 | def u_run(*args, strip_eval=False):
230 | sout, _e = exec_qdep("update", "--qmake", qmake_path, "--make", make_path, *args, keep_stdout=strip_eval)
231 | if strip_eval:
232 | return list(map(lambda s: s.strip(), sout.strip().split("\n")))
233 | else:
234 | return None
235 |
236 | with open("normal.pro", "w") as pro_file:
237 | pro_file.write("QDEP_DEPENDS += Skycoder42/qpmx-sample-package@1.0.0/qpmx-sample-package.prc\n")
238 | pro_file.write("QDEP_DEPENDS += Skycoder42/qpmx-sample-package\n")
239 | pro_file.write("QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n\n")
240 | exec_qdep("init", os.path.abspath("normal.pro"))
241 | u_res = u_run(os.path.abspath("normal.pro"), strip_eval=True)
242 | assert len(u_res) == 7
243 | assert u_res[1] == "Found a new version for package Skycoder42/qpmx-sample-package: 1.0.0 -> 1.2.0"
244 | assert u_res[4:] == [
245 | "QDEP_DEPENDS = Skycoder42/qpmx-sample-package@1.2.0/qpmx-sample-package.prc \\",
246 | "Skycoder42/qpmx-sample-package \\",
247 | "Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri"
248 | ]
249 |
250 | u_run("--replace", os.path.abspath("normal.pro"))
251 | with open("normal.pro", "a") as pro_file:
252 | pro_file.write("write_file($$PWD/normal.txt, QDEP_DEPENDS)\n")
253 | exec_qmake("normal.pro")
254 | with open("normal.txt", "r") as txt_file:
255 | txt_lines = txt_file.readlines()
256 | assert txt_lines == [
257 | "Skycoder42/qpmx-sample-package@1.2.0/qpmx-sample-package.prc\n",
258 | "Skycoder42/qpmx-sample-package\n",
259 | "Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n"
260 | ]
261 |
262 | with open("eval.pro", "w") as pro_file:
263 | pro_file.write("TEMPLATE = subdirs\n")
264 | pro_file.write("SUBDIRS += sub_eval\n")
265 | os.mkdir("sub_eval")
266 | with open("sub_eval/sub_eval.pro", "w") as pro_file:
267 | pro_file.write("TEMPLATE = aux\n")
268 | pro_file.write("QDEP_DEPENDS += Skycoder42/qpmx-sample-package@1.0.0/qpmx-sample-package.prc\n")
269 | pro_file.write("QDEP_DEPENDS += Skycoder42/qpmx-sample-package\n")
270 | pro_file.write("QDEP_DEPENDS += Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n\n")
271 | exec_qdep("init", os.path.abspath("sub_eval/sub_eval.pro"))
272 | u_res = u_run("--eval", os.path.abspath("eval.pro"), strip_eval=True)
273 | print(len(u_res), u_res)
274 | assert len(u_res) == 8
275 | assert u_res[2] == "Found a new version for package Skycoder42/qpmx-sample-package: 1.0.0 -> 1.2.0"
276 | assert u_res[5:] == [
277 | "QDEP_DEPENDS = Skycoder42/qpmx-sample-package \\",
278 | "Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri \\",
279 | "Skycoder42/qpmx-sample-package@1.2.0/qpmx-sample-package.prc"
280 | ]
281 |
282 | u_run("--eval", "--replace", os.path.abspath("eval.pro"))
283 | with open("sub_eval/sub_eval.pro", "a") as pro_file:
284 | pro_file.write("write_file($$PWD/../eval.txt, QDEP_DEPENDS)\n")
285 | exec_qmake("sub_eval/sub_eval.pro")
286 | with open("eval.txt", "r") as txt_file:
287 | txt_lines = txt_file.readlines()
288 | assert txt_lines == [
289 | "Skycoder42/qpmx-sample-package@1.2.0/qpmx-sample-package.prc\n",
290 | "Skycoder42/qpmx-sample-package\n",
291 | "Skycoder42/qdep@master/tests/packages/basic/package1/package1.pri\n"
292 | ]
293 |
294 |
295 | def test_clear(): # TODO implement later
296 | pass
297 |
298 |
299 | def test_run(name, test_fn, **kwargs):
300 | if tests_allowed is not None and name not in tests_allowed:
301 | print("\n*** Skipping test:", name)
302 | return
303 |
304 | print("\n*** Running test:", name)
305 | os.makedirs(name)
306 | os.chdir(name)
307 |
308 | test_fn(**kwargs)
309 |
310 | os.chdir("..")
311 | print("*** SUCCESS")
312 |
313 |
314 | if __name__ == '__main__':
315 | assert len(sys.argv) > 2
316 | qmake_path = sys.argv[1]
317 | make_path = sys.argv[2]
318 |
319 | if len(sys.argv) > 3:
320 | is_local_run = True
321 | qdep_path = os.path.join(os.path.dirname(__file__), "..", "..", "testentry.py")
322 | tests_allowed = sys.argv[4:]
323 | if len(tests_allowed) == 0:
324 | tests_allowed = None
325 | else:
326 | qdep_path = shutil.which("qdep")
327 | if qdep_path is None:
328 | print("Failed to find qdep. Current path is:", os.environ["PATH"], file=sys.stderr)
329 | sys.exit(1)
330 |
331 | print("qdep path:", qdep_path)
332 | print("qmake path:", qmake_path)
333 |
334 | cwd = os.path.join(os.getcwd(), "tests")
335 | if os.path.exists(cwd):
336 | shutil.rmtree(cwd)
337 | os.makedirs(cwd)
338 | os.chdir(cwd)
339 |
340 | test_run("prfgen", test_prfgen)
341 | test_run("init", test_init)
342 | test_run("lupdate", test_lupdate)
343 | test_run("versions", test_versions)
344 | test_run("query", test_query)
345 | test_run("get", test_get)
346 | test_run("update", test_update)
347 | test_run("clear", test_clear)
348 |
--------------------------------------------------------------------------------
/qdep/internal/cli.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 | import argcomplete
4 |
5 | from qdep.qdep import *
6 | from qdep.internal.private import *
7 |
8 |
9 | def complete_path(prefix, filter_fn):
10 | def map_dir_contents(base_dir, file):
11 | full_path = path.join(base_dir, file)
12 | if path.isdir(full_path):
13 | return full_path + "/"
14 | else:
15 | return full_path
16 |
17 | path_base, path_name = path.split(prefix)
18 | if len(path_base) == 0:
19 | path_base = "."
20 | return map(lambda f: map_dir_contents(path_base, f), filter(lambda f: filter_fn(path_base, path_name, f), os.listdir(path_base)))
21 |
22 |
23 | def complete_executable(prefix, limiter=None):
24 | def filter_dir_contents(base_dir, base_name, file):
25 | if path.isdir(path.join(base_dir, file)):
26 | return True
27 | elif file.startswith(base_name) and (limiter is None or limiter in file):
28 | return True
29 | else:
30 | return False
31 |
32 | return complete_path(prefix, filter_dir_contents)
33 |
34 |
35 | def complete_suffix(prefix, suffix):
36 | def filter_dir_contents(base_dir, base_name, file):
37 | if path.isdir(path.join(base_dir, file)):
38 | return True
39 | elif file.startswith(base_name) and file.endswith(suffix):
40 | return True
41 | else:
42 | return False
43 |
44 | return complete_path(prefix, filter_dir_contents)
45 |
46 |
47 | def qmake_completer(prefix, **kwargs):
48 | return complete_executable(prefix, "qmake")
49 |
50 |
51 | def make_completer(prefix, **kwargs):
52 | return complete_executable(prefix, "make")
53 |
54 |
55 | def dir_completer(prefix, **kwargs):
56 | def filter_dir_contents(base_dir, base_name, file):
57 | return path.isdir(path.join(base_dir, file)) and file.startswith(base_name)
58 |
59 | return complete_path(prefix, filter_dir_contents)
60 |
61 |
62 | def pro_completer(prefix, **kwargs):
63 | return complete_suffix(prefix, ".pro")
64 |
65 |
66 | def pri_completer(prefix, **kwargs):
67 | return complete_suffix(prefix, ".pri")
68 |
69 |
70 | def main():
71 | parser = argparse.ArgumentParser(description="A very basic yet simple to use dependency management tool for qmake based projects.")
72 | parser.add_argument("--version", action="version", version=version)
73 | parser.add_argument("--trace", action="store_true", help="In case of an exception, print the whole stack trace")
74 |
75 | sub_args = parser.add_subparsers(dest="operation", title="Operations", metavar="{operation}")
76 |
77 | prfgen_parser = sub_args.add_parser("prfgen", help="Generate a qmake project feature (prf) for the given qmake.")
78 | prfgen_parser.add_argument("--qmake", action="store", default="qmake", help="The path to a qmake executable to place the prf file for.").completer = qmake_completer
79 | prfgen_parser.add_argument("-d", "--dir", dest="dir", action="store", help="The directory containing the mkspec folder where to place the prf file. If not specified, qmake is queried form the location.").completer = dir_completer
80 |
81 | init_parser = sub_args.add_parser("init", help="Initialize a pro file to use qdep by adding the required lines.")
82 | init_parser.add_argument("profile", help="The path to the pro file to add the qdep code to.").completer = pro_completer
83 |
84 | lupdate_parser = sub_args.add_parser("lupdate", help="Run lupdate for the QDEP_TRANSLATION variable in a given pri file.")
85 | lupdate_parser.add_argument("--qmake", action="store", default="qmake", help="The path to a qmake executable to find the corresponding lupdate for.").completer = qmake_completer
86 | lupdate_parser.add_argument("--pri-file", dest="pri_path", action="store", required=True, help="The path to the pri-file that contains a QDEP_TRANSLATIONS variable, to generate translations for.").completer = pri_completer
87 | lupdate_parser.add_argument("largs", action="store", nargs="*", metavar="lupdate-argument", help="Additionals arguments to be passed to lupdate. MUST be proceeded by '--'!")
88 |
89 | clear_parser = sub_args.add_parser("clear", help="Remove all sources from the users global cache.")
90 | clear_parser.add_argument("-y", "--yes", dest="yes", action="store_true", help="Immediatly remove the caches, without asking for confirmation first.")
91 |
92 | versions_parser = sub_args.add_parser("versions", help="List all known versions/tags of the given package")
93 | versions_parser.add_argument("-b", "--branches", dest="branches", action="store_true", help="Include branches into the output.")
94 | versions_parser.add_argument("--no-tags", dest="tags", action="store_false", help="Exclude tags from the output.")
95 | versions_parser.add_argument("-s", "--short", dest="short", action="store_true", help="Print output as a single, uncommented line")
96 | versions_parser.add_argument("--limit", action="store", type=int, help="Limit the returned lists to the LIMIT newest entries per type.")
97 | versions_parser.add_argument("package", help="The package to list the versions for. Specify without a version of pro/pri file path!")
98 |
99 | query_parser = sub_args.add_parser("query", help="Query details about a given package identifier")
100 | query_parser.add_argument("--expand", action="store_true", help="Only expand the package name, don't output anything else.")
101 | query_parser.add_argument("--no-check", dest="check", action="store_false", help="Do not check if the package actually exists.")
102 | query_parser.add_argument("--versions", action="store_true", help="Also query and display all available tags and branches. See 'qdep.py versions' for alternative formats.")
103 | query_parser.add_argument("package", help="The package to query information for.")
104 |
105 | get_parser = sub_args.add_parser("get", help="Download the sources of one ore more packages into the source cache.")
106 | get_parser.add_argument("--extract", action="store_true", help="Run in pro-file mode. Arguments are interpreted as pro files and are scanned for dependencies")
107 | get_parser.add_argument("--eval", action="store_true", help="Fully evaluate all pro files by running qmake on them. Implies '--extract'.")
108 | get_parser.add_argument("--no-recurse", dest="recurse", action="store_false", help="Do not scan downloaded packages for further dependencies.")
109 | get_parser.add_argument("--qmake", action="store", default="qmake", help="The path to a qmake executable to use for evaluation if '--eval' was specified.").completer = qmake_completer
110 | get_parser.add_argument("--make", action="store", default="make", help="The path to a make executable to use for evaluation if '--eval' was specified.").completer = make_completer
111 | get_parser.add_argument("-d", "--dir", "--cache-dir", dest="dir", action="store", help="Specify the directory where to download the sources to. Shorthand for using the QDEP_CACHE_DIR environment variable.").completer = dir_completer
112 | get_parser.add_argument("args", nargs="+", help="The packages (or pro files if using '--extract') to download the sources for.").completer = pro_completer
113 |
114 | update_parser = sub_args.add_parser("update", help="Check for newer versions of used packages and optionally update them.")
115 | update_parser.add_argument("--eval", action="store_true", help="Fully evaluate all pro files by running qmake on them.")
116 | update_parser.add_argument("--qmake", action="store", default="qmake", help="The path to a qmake executable to use for evaluation if '--eval' was specified.").completer = qmake_completer
117 | update_parser.add_argument("--make", action="store", default="make", help="The path to a make executable to use for evaluation if '--eval' was specified.").completer = make_completer
118 | update_parser.add_argument("--replace", action="store_true", help="Automatically replace newer packages in the evaluated project files instead of printing to the console.")
119 | update_parser.add_argument("profile", metavar="pro-file", help="The qmake pro-file to update dependencies for.").completer = pro_completer
120 |
121 | dephash_parser = sub_args.add_parser("dephash", help="[INTERNAL] Generated unique identifying hashes for qdep packages.")
122 | dephash_parser.add_argument("--project", action="store_true", help="Interpret input as a project dependency, not a normal pri dependency.")
123 | dephash_parser.add_argument("--pkgpath", action="store_true", help="Return the hash and the pro/pri subpath as tuple, seperated by a ';'.")
124 | dephash_parser.add_argument("input", action="store", nargs="*", metavar="package", help="The packages to generate hashes for.")
125 |
126 | pkgresolve_parser = sub_args.add_parser("pkgresolve", help="[INTERNAL] Download the given qdep package and extract relevant information from it.")
127 | pkgresolve_parser.add_argument("--no-pull", dest="pull", action="store_false", help="Do not update existing packages that are based on branches instead of tags.")
128 | pkgresolve_parser.add_argument("--no-clone", dest="clone", action="store_false", help="Do not allow installation of new packages. Trying so will lead to an error. Updating existing packages is still possible.")
129 | pkgresolve_parser.add_argument("--project", dest="project", action="store_true", help="Interpret input as a project dependency, not a normal pri dependency")
130 | pkgresolve_parser.add_argument("package", action="store", metavar="package", help="The package identifier of the package to be downloaded and resolved.")
131 | pkgresolve_parser.add_argument("version", action="store", nargs="?", metavar="latest-version", help="The previously cached version for packages with no version identifier.")
132 |
133 | hookgen_parser = sub_args.add_parser("hookgen", help="[INTERNAL] Generate a header file with a method to load all resource hooks.")
134 | hookgen_parser.add_argument("--hooks", action="store", nargs="*", help="The names of additional hook functions to be referenced.")
135 | hookgen_parser.add_argument("prefix", action="store", help="The target name to use as part of the generated hook method.")
136 | hookgen_parser.add_argument("header", action="store", help="The path to the header-file to be generated.")
137 | hookgen_parser.add_argument("dummy", action="store", metavar="pro-file", help="The path to the current pro file - needed for Makefile rules.")
138 | hookgen_parser.add_argument("resources", action="store", nargs="*", metavar="resource", help="Paths to the resource-files to generate the hooks for.")
139 |
140 | hookimp_parser = sub_args.add_parser("hookimp", help="[INTERNAL] Generate a source file that includes and runs all qdep hooks as normal startup hook.")
141 | hookimp_parser.add_argument("--hooks", action="store", nargs="*", help="The names of additional hook functions to be referenced.")
142 | hookimp_parser.add_argument("outfile", action="store", help="The path to the cpp-file to be generated.")
143 | hookimp_parser.add_argument("dummy", action="store", metavar="pro-file", help="The path to the current pro file - needed for Makefile rules.")
144 | hookimp_parser.add_argument("headers", action="store", nargs="*", metavar="header", help="Paths to the header-files that contain hooks to be run.")
145 |
146 | lconvert_parser = sub_args.add_parser("lconvert", help="[INTERNAL] Combine ts files with translations from qdep packages.")
147 | lconvert_parser.add_argument("--combine", action="store", nargs="*", help="The qdep ts files that should be combined into the real ones.")
148 | lconvert_parser.add_argument("tsfile", action="store", help="The path to the ts file to combine with the qdep ts files.")
149 | lconvert_parser.add_argument("outfile", action="store", help="The path to the ts file to be generated.")
150 | lconvert_parser.add_argument("largs", action="store", nargs="+", metavar="lconvert-tool", help="Path to the lconvert tool as well as additional arguments to it.")
151 |
152 | prolink_parser = sub_args.add_parser("prolink", help="[INTERNAL] Resolve the path a linked project dependency would be at.")
153 | prolink_parser.add_argument("prodir", action="store", help="The directory of the pro file that includes the other one.")
154 | prolink_parser.add_argument("pkghash", action="store", help="The hash identifier of the project to link.")
155 | prolink_parser.add_argument("pkgpath", action="store", help="The path to the pro file within the dependency.")
156 | prolink_parser.add_argument("--link", action="store", help="Perform the link operation and create the symlink/dirtree, based on the given path to the dependency sources.")
157 |
158 | argcomplete.autocomplete(parser)
159 | res = parser.parse_args()
160 |
161 | try:
162 | if res.operation == "prfgen":
163 | prfgen(path.abspath(sys.argv[0]), res.qmake, res.dir)
164 | elif res.operation == "init":
165 | init(res.profile)
166 | elif res.operation == "lupdate":
167 | lupdate(res.pri_path, res.qmake, res.largs)
168 | elif res.operation == "clear":
169 | clear(res.yes)
170 | elif res.operation == "versions":
171 | versions(res.package, res.tags, res.branches, res.short, res.limit)
172 | elif res.operation == "query":
173 | query(res.package, res.check, res.versions, res.expand)
174 | elif res.operation == "get":
175 | get(*res.args, extract=res.extract, evaluate=res.eval, recurse=res.recurse, qmake=res.qmake, make=res.make, cache_dir=res.dir)
176 | elif res.operation == "update":
177 | update(res.profile, res.eval, res.replace, res.qmake, res.make)
178 | elif res.operation == "dephash":
179 | dephash(*res.input, project=res.project, pkgpath=res.pkgpath)
180 | elif res.operation == "pkgresolve":
181 | pkgresolve(res.package, res.version, res.project, res.pull, res.clone)
182 | elif res.operation == "hookgen":
183 | hookgen(res.prefix, res.header, res.resources, res.hooks)
184 | elif res.operation == "hookimp":
185 | hookimp(res.outfile, res.headers, res.hooks)
186 | elif res.operation == "lconvert":
187 | lconvert(res.tsfile, res.outfile, *res.combine, lconvert_args=res.largs)
188 | elif res.operation == "prolink":
189 | prolink(res.prodir, res.pkghash, res.pkgpath, res.link)
190 | else:
191 | parser.print_help()
192 | return 1
193 |
194 | return 0
195 | except Exception as exception:
196 | if res.trace or True:
197 | raise
198 | else:
199 | print(exception, file=sys.stderr)
200 | return 1
201 |
202 |
203 | if __name__ == '__main__':
204 | sys.exit(main())
205 |
--------------------------------------------------------------------------------
/qdep/internal/prf.py:
--------------------------------------------------------------------------------
1 | qdep_prf = """isEmpty(QDEP_TOOL) QDEP_TOOL = $$system_path($$QDEP_PATH)
2 |
3 | # verify versions are correct
4 | __qdep_script_version = $$system($$QDEP_TOOL --version)
5 | !equals(QDEP_VERSION, $$__qdep_script_version):error("qdep.py script version ($$__qdep_script_version) is different to qdep.prf version ($$QDEP_VERSION)! Run '$$QDEP_TOOL prfgen --qmake $$QMAKE_QMAKE' to update the prf file!")
6 |
7 | # set some variables
8 | isEmpty(QDEP_CACHE_SCOPE): QDEP_CACHE_SCOPE = stash
9 |
10 | isEmpty(QDEP_GENERATED_DIR): QDEP_GENERATED_DIR = $$OUT_PWD
11 | debug_and_release:CONFIG(release, debug|release): QDEP_GENERATED_SOURCES_DIR = $${QDEP_GENERATED_DIR}/release
12 | else:debug_and_release:CONFIG(debug, debug|release): QDEP_GENERATED_SOURCES_DIR = $${QDEP_GENERATED_DIR}/debug
13 | else: QDEP_GENERATED_SOURCES_DIR = $$QDEP_GENERATED_DIR
14 | isEmpty(QDEP_GENERATED_TS_DIR): QDEP_GENERATED_TS_DIR = $$QDEP_GENERATED_DIR/.qdepts
15 | debug_and_release:CONFIG(release, debug|release): QDEP_GENERATED_TS_DIR = $${QDEP_GENERATED_TS_DIR}/release
16 | else:debug_and_release:CONFIG(debug, debug|release): QDEP_GENERATED_TS_DIR = $${QDEP_GENERATED_TS_DIR}/debug
17 | isEmpty(QDEP_LUPDATE) {
18 | qtPrepareTool(QDEP_LUPDATE, lupdate)
19 | QDEP_LUPDATE += -recursive -locations relative
20 | qdep_lupdate_no_obsolete: QDEP_LUPDATE += -no-obsolete
21 | }
22 | isEmpty(QDEP_LCONVERT) {
23 | qtPrepareTool(QDEP_LCONVERT, lconvert)
24 | QDEP_LCONVERT += -sort-contexts
25 | }
26 |
27 | isEmpty(QDEP_EXPORT_PATH): QDEP_EXPORT_PATH = $$QDEP_GENERATED_DIR
28 | debug_and_release:CONFIG(release, debug|release): QDEP_EXPORT_PATH = $$QDEP_EXPORT_PATH/release
29 | debug_and_release:CONFIG(debug, debug|release): QDEP_EXPORT_PATH = $$QDEP_EXPORT_PATH/debug
30 | isEmpty(QDEP_EXPORT_NAME) {
31 | __qdep_base_name = $$basename(_PRO_FILE_)
32 | QDEP_EXPORT_NAME = $$replace(__qdep_base_name, "\\\\.[^\\\\.]*$", "_export.pri")
33 | }
34 |
35 | isEmpty(__QDEP_PRIVATE_SEPERATOR): __QDEP_PRIVATE_SEPERATOR = "==="
36 | isEmpty(__QDEP_TUPLE_SEPERATOR): __QDEP_TUPLE_SEPERATOR = "---"
37 |
38 | CONFIG += qdep_build
39 | DEFINES += QDEP_BUILD
40 |
41 | # The primary dependecy collector function
42 | defineTest(qdepCollectDependencies) {
43 | # transform all dependencies into unique hashes
44 | qdep_dependencies =
45 | for(arg, ARGS): qdep_dependencies += $$system_quote($$arg)
46 | qdep_ok =
47 | qdep_hashes = $$system($$QDEP_TOOL dephash $$qdep_dependencies, lines, qdep_ok)
48 | !equals(qdep_ok, 0):return(false)
49 |
50 | for(dep_hash, qdep_hashes) {
51 | # handle each dependency, but each package only once
52 | dep_pkg = $$take_first(ARGS)
53 | !contains(__QDEP_INCLUDE_CACHE, $$dep_hash) {
54 | # Install the sources and extract some parameters
55 | dep_extra_args =
56 | qdep_no_pull: dep_extra_args += --no-pull
57 | qdep_no_clone: dep_extra_args += --no-clone
58 | !isEmpty($${dep_hash}.version): dep_vers_arg = $$system_quote($$first($${dep_hash}.version))
59 | else: dep_vers_arg =
60 | qdep_ok =
61 | dep_data = $$system($$QDEP_TOOL pkgresolve $$dep_extra_args $$system_quote($$dep_pkg) $$dep_vers_arg, lines, qdep_ok)
62 | !equals(qdep_ok, 0):return(false)
63 | !equals(dep_hash, $$take_first(dep_data)):error("Critical internal error: dependencies out of sync"):return(false)
64 |
65 | dep_version = $$take_first(dep_data)
66 | dep_base = $$take_first(dep_data)
67 | dep_path = $$take_first(dep_data)
68 | dep_needs_cache = $$take_first(dep_data)
69 |
70 | $${dep_hash}.package = $$dep_pkg
71 | $${dep_hash}.version = $$dep_version
72 | $${dep_hash}.path = $${dep_base}$${dep_path}
73 | $${dep_hash}.exports =
74 | $${dep_hash}.local = 1
75 | export($${dep_hash}.package)
76 | export($${dep_hash}.version)
77 | export($${dep_hash}.path)
78 | export($${dep_hash}.local)
79 |
80 | # Cache the package version for dependencies with undetermined versions
81 | !qdep_no_cache:equals(dep_needs_cache, True):!cache($${dep_hash}.version, set $$QDEP_CACHE_SCOPE):warning("Failed to cache package version for $$dep_pkg")
82 |
83 | # Find all dependencies that package depends on and call this method recursively for those
84 | sub_deps = $$fromfile($$eval($${dep_hash}.path), QDEP_DEPENDS)
85 | __QDEP_REAL_DEPS_STACK += $$dep_hash
86 | !isEmpty(sub_deps):!qdepCollectDependencies($$sub_deps):return(false)
87 | __QDEP_INCLUDE_CACHE *= $$take_last(__QDEP_REAL_DEPS_STACK)
88 | export(__QDEP_INCLUDE_CACHE)
89 |
90 | # Handle all defines for symbol exports, if specified
91 | sub_exports = $$fromfile($$eval($${dep_hash}.path), QDEP_PACKAGE_EXPORTS)
92 |
93 | qdep_export_all|contains(QDEP_EXPORTS, $$dep_pkg) {
94 | !static:!staticlib:for(sub_export, sub_exports) {
95 | DEFINES += "$${sub_export}=Q_DECL_EXPORT"
96 | QDEP_EXPORTED_DEFINES += "$${sub_export}=Q_DECL_IMPORT"
97 | $${dep_hash}.exports += $$sub_export
98 | } else:for(sub_export, sub_exports) {
99 | DEFINES += "$${sub_export}="
100 | QDEP_EXPORTED_DEFINES += "$${sub_export}="
101 | }
102 | } else:for(sub_export, sub_exports): \\
103 | DEFINES += "$${sub_export}="
104 | export(DEFINES)
105 | export(QDEP_EXPORTED_DEFINES)
106 | export($${dep_hash}.exports)
107 | } else: \\
108 | !equals($${dep_hash}.package, $$dep_pkg): \\
109 | warning("Detected includes of multiple different versions of the same dependency. Package \\"$$first($${dep_hash}.package)\\" is used, and package \\"$$dep_pkg\\" was detected.")
110 | }
111 |
112 | return(true)
113 | }
114 |
115 | # The dependecy collector function for project dependencies in subdirs projects
116 | defineTest(qdepCollectProjectDependencies) {
117 | # transform all dependencies into unique hashes
118 | qdep_dependencies =
119 | for(arg, ARGS): qdep_dependencies += $$system_quote($$arg)
120 | qdep_ok =
121 | qdep_hashes = $$system($$QDEP_TOOL dephash --project $$qdep_dependencies, lines, qdep_ok)
122 | !equals(qdep_ok, 0):return(false)
123 |
124 | for(dep_hash, qdep_hashes) {
125 | # handle each dependency, but each package only once
126 | dep_pkg = $$take_first(ARGS)
127 | !contains(__QDEP_INCLUDE_CACHE, $$dep_hash) {
128 | # Install the sources and extract some parameters
129 | dep_extra_args =
130 | qdep_no_pull: dep_extra_args += --no-pull
131 | qdep_no_clone: dep_extra_args += --no-clone
132 | !isEmpty($${dep_hash}.version): dep_vers_arg = $$system_quote($$first($${dep_hash}.version))
133 | else: dep_vers_arg =
134 | qdep_ok =
135 | dep_data = $$system($$QDEP_TOOL pkgresolve --project $$dep_extra_args $$system_quote($$dep_pkg) $$dep_vers_arg, lines, qdep_ok)
136 | !equals(qdep_ok, 0):return(false)
137 | !equals(dep_hash, $$take_first(dep_data)):error("Critical internal error: project dependencies out of sync"):return(false)
138 |
139 | dep_version = $$take_first(dep_data)
140 | dep_base = $$take_first(dep_data)
141 | dep_path = $$take_first(dep_data)
142 | dep_needs_cache = $$take_first(dep_data)
143 |
144 | $${dep_hash}.package = $$dep_pkg
145 | $${dep_hash}.version = $$dep_version
146 | $${dep_hash}.path = $${dep_base}$${dep_path}
147 | $${dep_hash}.exports =
148 | $${dep_hash}.local = 0
149 | $${dep_hash}.target = sub$${dep_hash}
150 | export($${dep_hash}.package)
151 | export($${dep_hash}.version)
152 | export($${dep_hash}.path)
153 | export($${dep_hash}.local)
154 | export($${dep_hash}.target)
155 |
156 | # Cache the package version for dependencies with undetermined versions
157 | !qdep_no_cache:equals(dep_needs_cache, True):!cache($${dep_hash}.version, set $$QDEP_CACHE_SCOPE):warning("Failed to cache package version for $$dep_pkg")
158 |
159 | # link the project and write the version if not already there
160 | exists("$$_PRO_FILE_PWD_/.qdep/$${dep_hash}/.version"): linked_version = $$cat("$$_PRO_FILE_PWD_/.qdep/$${dep_hash}/.version", lines)
161 | else: linked_version =
162 | qdep_ok =
163 | !equals($${dep_hash}.version, $$linked_version) {
164 | $${dep_hash}.file = $$system($$QDEP_TOOL prolink --link $$system_quote($${dep_base}) $$system_quote($$_PRO_FILE_PWD_) $${dep_hash} $$system_quote($${dep_path}), lines, qdep_ok)
165 | !equals(qdep_ok, 0):return(false)
166 | !write_file("$$_PRO_FILE_PWD_/.qdep/$${dep_hash}/.version", $${dep_hash}.version): \\
167 | error("Failed to cache project dependency version for $$first($${dep_hash}.package)")
168 | } else {
169 | $${dep_hash}.file = $$system($$QDEP_TOOL prolink $$system_quote($$_PRO_FILE_PWD_) $${dep_hash} $$system_quote($${dep_path}), lines, qdep_ok)
170 | !equals(qdep_ok, 0):return(false)
171 | }
172 | export($${dep_hash}.file)
173 |
174 | # Find all further project dependencies that package depends on
175 | sub_deps = $$fromfile($$eval($${dep_hash}.path), QDEP_PROJECT_DEPENDS)
176 | !isEmpty(sub_deps) {
177 | qdep_ok =
178 | $${dep_hash}.depends = $$system($$QDEP_TOOL dephash --project $$sub_deps, lines, qdep_ok)
179 | !equals(qdep_ok, 0):return(false)
180 | export($${dep_hash}.depends)
181 | }
182 | SUBDIRS += $${dep_hash}
183 | export(SUBDIRS)
184 | __QDEP_INCLUDE_CACHE += $${dep_hash}
185 | export(__QDEP_INCLUDE_CACHE)
186 |
187 | # calls this method recursively for all dependencies
188 | !qdepCollectProjectDependencies($$sub_deps):return(false)
189 | } else: \\
190 | !equals($${dep_hash}.package, $$dep_pkg): \\
191 | warning("Detected includes of multiple different versions of the same dependency. Package \\"$$first($${dep_hash}.package)\\" is used, and package \\"$$dep_pkg\\" was detected.")
192 | }
193 |
194 | return(true)
195 | }
196 |
197 | # resolve qdep_depends of subdir dependencies to the actual hashes
198 | defineTest(qdepResolveSubdirDepends) {
199 | for(subproj, 1) {
200 | !isEmpty($${subproj}.qdep_depends) {
201 | qdep_dependencies =
202 | for(arg, $${subproj}.qdep_depends): qdep_dependencies += $$system_quote($$arg)
203 | qdep_ok =
204 | $${subproj}.depends += $$system($$QDEP_TOOL dephash --project $$qdep_dependencies, lines, qdep_ok)
205 | !equals(qdep_ok, 0):return(false)
206 | export($${subproj}.depends)
207 | }
208 | }
209 | return(true)
210 | }
211 |
212 | # pass the root dir and dependencies and create QDEP_LINK_DEPENDS values from it
213 | defineReplace(qdepResolveProjectLinkDeps) {
214 | qdep_dependencies =
215 | for(arg, 2): qdep_dependencies += $$system_quote($$arg)
216 | qdep_ok =
217 | dep_tuples = $$system($$QDEP_TOOL dephash --project --pkgpath $$qdep_dependencies, lines, qdep_ok)
218 | !equals(qdep_ok, 0):return()
219 |
220 | link_paths =
221 | for(qdep_tuple, dep_tuples) {
222 | tpl_args = $$split(qdep_tuple, ";")
223 | dep_hash = $$take_first(tpl_args)
224 | dep_path = $$join(tpl_args, ";")
225 | qdep_ok =
226 | link_paths += $$system($$QDEP_TOOL prolink $$system_quote($$absolute_path($$1, $$_PRO_FILE_PWD_)) $${dep_hash} $$system_quote($${dep_path}), lines, qdep_ok)
227 | !equals(qdep_ok, 0):return()
228 | }
229 |
230 | return($$link_paths)
231 | }
232 |
233 | # Write a quoted value for the given variable name as a single value
234 | defineReplace(qdepOutQuote) {
235 | result =
236 | var_name = $$1
237 | isEmpty(4): \\
238 | intendent = $$escape_expand(\\t)
239 | else {
240 | intendent =
241 | for(x, "1..$$4"): \\
242 | intendent = "$$intendent$$escape_expand(\\t)"
243 | }
244 | equals(3, prepend): \\
245 | for(value, 2): result += "$$intendent$$var_name = $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}quote($$value) $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}$$var_name"
246 | else:equals(3, star): \\
247 | for(value, 2): result += "$$intendent$$var_name *= $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}quote($$value)"
248 | else:equals(3, pop): \\
249 | for(value, 2): result += "$$intendent$${LITERAL_DOLLAR}$${LITERAL_DOLLAR}take_last($$var_name)"
250 | else: \\
251 | for(value, 2): result += "$$intendent$$var_name += $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}quote($$value)"
252 | return($$result)
253 | }
254 |
255 | # A function to create a pri file to include the library and all exported
256 | defineTest(qdepCreateExportPri) {
257 | # write include guard
258 | out_file_data = "!contains(__QDEP_EXPORT_QMAKE_INCLUDE_GUARD, $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}PWD) {"
259 | out_file_data += $$qdepOutQuote(__QDEP_EXPORT_QMAKE_INCLUDE_GUARD, $${LITERAL_DOLLAR}$${LITERAL_DOLLAR}PWD)
260 |
261 | # write dependencies
262 | !static:!staticlib: \\
263 | out_file_data += $$qdepOutQuote(__QDEP_IS_DYNAMIC_EXPORT_INCLUDE_STACK, true)
264 | for(link_dep, QDEP_LINK_DEPENDS): \\
265 | out_file_data += "$$escape_expand(\\t)include($$qdepLinkExpand($$link_dep))"
266 | !static:!staticlib: \\
267 | out_file_data += $$qdepOutQuote(__QDEP_IS_DYNAMIC_EXPORT_INCLUDE_STACK, true, pop)
268 |
269 | # write basic variables
270 | out_file_data += $$qdepOutQuote(DEFINES, $$QDEP_EXPORTED_DEFINES $$QDEP_DEFINES)
271 | out_file_data += $$qdepOutQuote(INCLUDEPATH, $$QDEP_EXPORTED_INCLUDEPATH $$QDEP_INCLUDEPATH)
272 | out_file_data += $$qdepOutQuote(LIBS, $$QDEP_EXPORTED_LIBS $$QDEP_LIBS)
273 | for(exp_var_key, QDEP_VAR_EXPORTS): out_file_data += $$qdepOutQuote($$exp_var_key, $$eval($$exp_var_key))
274 |
275 | # write package cache
276 | for(dep_hash, __QDEP_INCLUDE_CACHE):equals($${dep_hash}.local, 1) {
277 | out_file_data += $$qdepOutQuote($${dep_hash}.package, $$eval($${dep_hash}.package))
278 | out_file_data += $$qdepOutQuote($${dep_hash}.version, $$eval($${dep_hash}.version))
279 | out_file_data += $$qdepOutQuote($${dep_hash}.path, $$eval($${dep_hash}.path))
280 | out_file_data += $$qdepOutQuote($${dep_hash}.exports, $$eval($${dep_hash}.exports))
281 | out_file_data += $$qdepOutQuote($${dep_hash}.local, 0)
282 | out_file_data += $$qdepOutQuote(__QDEP_INCLUDE_CACHE, $$dep_hash)
283 | }
284 |
285 | # write library linkage common parts
286 | isEmpty(DESTDIR) {
287 | out_libdir = $$OUT_PWD
288 | debug_and_release:CONFIG(release, debug|release): out_libdir = $${out_libdir}/release
289 | else:debug_and_release:CONFIG(debug, debug|release): out_libdir = $${out_libdir}/debug
290 | } else: out_libdir = $$absolute_path($$DESTDIR, $$OUT_PWD)
291 |
292 | !qdep_no_link {
293 | # write includepath
294 | out_file_data += $$qdepOutQuote(INCLUDEPATH, $$_PRO_FILE_PWD_)
295 |
296 | # write QT, PKGCONFIG and QDEP_LIBS (unless disabled)
297 | !qdep_no_export_link {
298 | qt: out_file_data += $$qdepOutQuote(QT, $$QT, star)
299 | link_pkgconfig: out_file_data += $$qdepOutQuote(PKGCONFIG, $$PKGCONFIG, star)
300 | }
301 | }
302 |
303 | # write static/dynamic specific library extra stuff
304 | static|staticlib:equals(TEMPLATE, lib) {
305 | out_file_data += "$$escape_expand(\\t)isEmpty(__QDEP_IS_DYNAMIC_EXPORT_INCLUDE_STACK) {"
306 |
307 | # write startup hooks
308 | debug_and_release:CONFIG(release, debug|release): out_file_data += "$$qdepOutQuote(__QDEP_HOOK_FILES, $$QDEP_GENERATED_DIR/release/qdep_$${TARGET}_hooks.h, "", 2)"
309 | else:debug_and_release:CONFIG(debug, debug|release): out_file_data += "$$qdepOutQuote(__QDEP_HOOK_FILES, $$QDEP_GENERATED_DIR/debug/qdep_$${TARGET}_hooks.h, "", 2)"
310 | else: out_file_data += "$$qdepOutQuote(__QDEP_HOOK_FILES, $$QDEP_GENERATED_DIR/qdep_$${TARGET}_hooks.h, "", 2)"
311 |
312 | # linkage
313 | !qdep_no_link {
314 | out_file_data += $$qdepOutQuote(DEPENDPATH, $$_PRO_FILE_PWD_, "", 2)
315 |
316 | win32-g++: out_file_data += $$qdepOutQuote(PRE_TARGETDEPS, "$${out_libdir}/lib$${TARGET}.a", "", 2)
317 | else:win32: out_file_data += $$qdepOutQuote(PRE_TARGETDEPS, "$${out_libdir}/$${TARGET}.lib", "", 2)
318 | else:unix: out_file_data += $$qdepOutQuote(PRE_TARGETDEPS, "$${out_libdir}/lib$${TARGET}.a", "", 2)
319 |
320 | out_file_data += $$qdepOutQuote(LIBS, "-l$${TARGET}", prepend, 2)
321 | out_file_data += $$qdepOutQuote(LIBS, "-L$${out_libdir}/", prepend, 2)
322 | }
323 |
324 | out_file_data += "$$escape_expand(\\t)}"
325 | } else {
326 | # linkage
327 | !qdep_no_link {
328 | equals(TEMPLATE, lib):out_file_data += $$qdepOutQuote(LIBS, "-l$${TARGET}", prepend)
329 | else {
330 | win32: bin_suffix = .exe
331 | out_file_data += $$qdepOutQuote(LIBS, "-l:$${TARGET}$${bin_suffix}", prepend)
332 | }
333 | out_file_data += $$qdepOutQuote(LIBS, "-L$${out_libdir}/", prepend)
334 | }
335 | }
336 |
337 | out_file_data += "}"
338 | write_file($$1, out_file_data):return(true)
339 | else:return(false)
340 | }
341 |
342 | # get the full pri path of a link dependency
343 | defineReplace(qdepLinkExpand) {
344 | base_path = $$1
345 | suffix = $$str_member($$base_path, -4, -1)
346 |
347 | !equals(suffix, ".pri") {
348 | equals(suffix, ".pro") {
349 | file_name = $$basename(base_path)
350 | file_name = "$$str_member($$file_name, 0, -5)_export.pri"
351 | base_path = $$dirname(base_path)
352 | } else {
353 | file_name = "$$basename(base_path)_export.pri"
354 | }
355 | debug_and_release:CONFIG(release, debug|release): base_path = $$base_path/release/$$file_name
356 | else:debug_and_release:CONFIG(debug, debug|release): base_path = $$base_path/debug/$$file_name
357 | else: base_path = $$base_path/$$file_name
358 | }
359 |
360 | base_path = $$absolute_path($$base_path, $$_PRO_FILE_PWD_)
361 | s_base_path = $$shadowed($$base_path)
362 | !isEmpty(s_base_path):exists($$s_base_path):return($$s_base_path)
363 | else:!isEmpty(base_path):exists($$base_path):return($$base_path)
364 | else:return()
365 | }
366 |
367 | # find the SUBDIRS project build directory that this qdep project was linked from
368 | defineReplace(qdepResolveLinkRoot) {
369 | path_segments = $$split(1, "/")
370 | path_count = $$size(path_segments)
371 | c_path = $$1
372 | check_next = 0
373 | for(index, 0..$$path_count) {
374 | equals(check_next, 1): \\
375 | exists("$$c_path/Makefile"): \\
376 | return($$c_path)
377 |
378 | c_name = $$basename(c_path)
379 | equals(c_name, ".qdep"): check_next = 1
380 | else: check_next = 0
381 | c_path = $$dirname(c_path)
382 | !exists($$c_path):return()
383 | }
384 | return()
385 | }
386 |
387 | # shell_escape a list of files
388 | defineReplace(qdepShellQuote) {
389 | out_files =
390 | for(input, ARGS): out_files += $$shell_quote($$absolute_path($$input, $$_PRO_FILE_PWD_))
391 | return($$out_files)
392 | }
393 |
394 | # dump dependencies for update collection
395 | defineTest(qdepDumpUpdateDeps) {
396 | dump_data = $$_PRO_FILE_
397 | dump_data += $$QDEP_DEPENDS
398 | dump_data += $$QDEP_PROJECT_SUBDIRS
399 | dump_data += $$QDEP_PROJECT_LINK_DEPENDS
400 | dump_data += $$QDEP_PROJECT_DEPENDS
401 | write_file($$OUT_PWD/qdep_depends.txt, dump_data):return(true)
402 | else:return(false)
403 | }
404 |
405 | # dump deps if in update mode
406 | __qdep_dump_dependencies: \\
407 | !qdepDumpUpdateDeps(): \\
408 | error("Failed to dump dependencies")
409 |
410 | # First transform project link depends to normal link depends
411 | !isEmpty(QDEP_PROJECT_ROOT): \\
412 | !isEmpty(QDEP_PROJECT_LINK_DEPENDS): \\
413 | QDEP_LINK_DEPENDS = $$qdepResolveProjectLinkDeps($$QDEP_PROJECT_ROOT, $$QDEP_PROJECT_LINK_DEPENDS) $$QDEP_LINK_DEPENDS # always prepend project link depends
414 |
415 | # Second transform project depends to normal link depends
416 | # This will only work if the project is imported as qdep project dependency
417 | !isEmpty(QDEP_PROJECT_DEPENDS) {
418 | __qdep_project_link_root = $$qdepResolveLinkRoot($$OUT_PWD)
419 | isEmpty(__qdep_project_link_root): warning("Failed to find including subdirs project - only use QDEP_PROJECT_DEPENDS in qdep project depenencies")
420 | else: QDEP_LINK_DEPENDS = $$qdepResolveProjectLinkDeps($$__qdep_project_link_root, $$QDEP_PROJECT_DEPENDS) $$QDEP_LINK_DEPENDS # always prepend project link depends
421 | }
422 |
423 | # Next collect all indirect dependencies
424 | # for GCC: create a link group, as these all add libs in arbitrary order
425 | !isEmpty(QDEP_LINK_DEPENDS): \\
426 | for(link_dep, QDEP_LINK_DEPENDS): \\
427 | !include($$qdepLinkExpand($$link_dep)): \\
428 | error("Failed to include linked library $$link_dep")
429 |
430 | # Collect all dependencies and then include them
431 | !isEmpty(QDEP_DEPENDS): {
432 | __qdep_cached_hooks = $$QDEP_HOOK_FNS
433 | QDEP_HOOK_FNS =
434 | __qdep_cached_resources = $$RESOURCES
435 | RESOURCES =
436 |
437 | !qdepCollectDependencies($$QDEP_DEPENDS):error("Failed to collect all dependencies")
438 | else:for(dep, __QDEP_INCLUDE_CACHE):equals($${dep}.local, 1) {
439 | __qdep_define_offset = $$size(DEFINES)
440 | __qdep_include_offset = $$size(INCLUDEPATH)
441 | __qdep_libs_offset = $$size(LIBS)
442 | include($$first($${dep}.path)) {
443 | qdep_export_all|contains(QDEP_EXPORTS, $$first($${dep}.package)) {
444 | QDEP_EXPORTED_DEFINES += $$member(DEFINES, $$__qdep_define_offset, -1)
445 | export(QDEP_EXPORTED_DEFINES)
446 | QDEP_EXPORTED_INCLUDEPATH += $$member(INCLUDEPATH, $$__qdep_include_offset, -1)
447 | export(QDEP_EXPORTED_INCLUDEPATH)
448 | QDEP_EXPORTED_LIBS += $$member(LIBS, $$__qdep_libs_offset, -1)
449 | export(QDEP_EXPORTED_LIBS)
450 | }
451 | } else:error("Failed to include pri file $$first($${dep}.package)")
452 | }
453 |
454 | QDEP_HOOK_FNS += $$__qdep_cached_hooks
455 | RESOURCES += $$__qdep_cached_resources
456 | }
457 |
458 | # Collect all project dependencies
459 | equals(TEMPLATE, subdirs):!isEmpty(QDEP_PROJECT_SUBDIRS) {
460 | !qdepCollectProjectDependencies($$QDEP_PROJECT_SUBDIRS): \\
461 | error("Failed to collect all project dependencies")
462 | !qdepResolveSubdirDepends($$SUBDIRS): \\
463 | error("Failed to link all project dependencies")
464 | }
465 |
466 | # create special target for resource hooks in static libs
467 | # or if not, reference their hooks of static libs (as special compiler, only for non-static apps/libs)
468 | static|staticlib:equals(TEMPLATE, lib) {
469 | __qdep_hook_generator_c.name = qdep hookgen ${QMAKE_FILE_IN}
470 | __qdep_hook_generator_c.input = _PRO_FILE_ RESOURCES
471 | __qdep_hook_generator_c.variable_out = HEADERS
472 | __qdep_hook_generator_c.commands = $$QDEP_PATH hookgen --hooks $$QDEP_HOOK_FNS -- $${TARGET} ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
473 | __qdep_hook_generator_c.output = $$QDEP_GENERATED_SOURCES_DIR/qdep_$${TARGET}_hooks.h
474 | __qdep_hook_generator_c.CONFIG += target_predeps combine no_link
475 | __qdep_hook_generator_c.depends += $$QDEP_PATH
476 | QMAKE_EXTRA_COMPILERS += __qdep_hook_generator_c
477 | } else:!equals(TEMPLATE, aux) {
478 | __qdep_hook_importer_c.name = qdep hookimp ${QMAKE_FILE_IN}
479 | __qdep_hook_importer_c.input = _PRO_FILE_ __QDEP_HOOK_FILES
480 | __qdep_hook_importer_c.variable_out = GENERATED_SOURCES
481 | __qdep_hook_importer_c.commands = $$QDEP_PATH hookimp --hooks $$QDEP_HOOK_FNS -- ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
482 | __qdep_hook_importer_c.output = $$QDEP_GENERATED_SOURCES_DIR/qdep_imported_hooks.cpp
483 | __qdep_hook_importer_c.CONFIG += target_predeps combine
484 | __qdep_hook_importer_c.depends += $$QDEP_PATH
485 | QMAKE_EXTRA_COMPILERS += __qdep_hook_importer_c
486 | }
487 |
488 | # Create lupdate target
489 | __qdep_lupdate_target.target = lupdate
490 | __qdep_lupdate_target.commands = $$QDEP_LUPDATE $$qdepShellQuote($$_PRO_FILE_PWD_ $$QDEP_LUPDATE_INPUTS) -ts $$qdepShellQuote($$TRANSLATIONS)
491 | __qdep_lupdate_target.depends += $$QDEP_LUPDATE_EXE $$_PRO_FILE_PWD_ $$QDEP_LUPDATE_INPUTS
492 | QMAKE_EXTRA_TARGETS += __qdep_lupdate_target
493 |
494 | # fix for broken lrelease make feature
495 | {
496 | isEmpty(LRELEASE_DIR): LRELEASE_DIR = .qm
497 | debug_and_release:CONFIG(release, debug|release): __qdep_lrelease_real_dir = $${LRELEASE_DIR}/release
498 | else:debug_and_release:CONFIG(debug, debug|release): __qdep_lrelease_real_dir = $${LRELEASE_DIR}/debug
499 | else: __qdep_lrelease_real_dir = $$LRELEASE_DIR
500 | __qdep_lrelease_real_dir = $$absolute_path($$__qdep_lrelease_real_dir, $$OUT_PWD)
501 | !exists($$__qdep_lrelease_real_dir): \\
502 | !mkpath($$__qdep_lrelease_real_dir): \\
503 | warning("Failed to create lrelease directory: $$__qdep_lrelease_real_dir")
504 | }
505 | qm_files.CONFIG += no_check_exist
506 |
507 | # Create special targets for translations
508 | !qdep_no_qm_combine {
509 | # move translations into temporary var for the compiler to work
510 | __QDEP_ORIGINAL_TRANSLATIONS = $$TRANSLATIONS
511 | TRANSLATIONS =
512 |
513 | # compiler for combined translations
514 | __qdep_qm_combine_c.name = qdep lrelease ${QMAKE_FILE_IN}
515 | __qdep_qm_combine_c.input = __QDEP_ORIGINAL_TRANSLATIONS
516 | __qdep_qm_combine_c.variable_out = TRANSLATIONS
517 | __qdep_qm_combine_c.commands = $$QDEP_PATH lconvert --combine $$qdepShellQuote($$QDEP_TRANSLATIONS) -- ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} $$QDEP_LCONVERT
518 | __qdep_qm_combine_c.output = $$QDEP_GENERATED_TS_DIR/${QMAKE_FILE_BASE}.ts
519 | __qdep_qm_combine_c.CONFIG += no_link
520 | __qdep_qm_combine_c.depends += $$QDEP_PATH $$QDEP_LCONVERT_EXE $$QDEP_TRANSLATIONS
521 | QMAKE_EXTRA_COMPILERS += __qdep_qm_combine_c
522 |
523 | # copy from lrelease.prf - needed as TRANSLATIONS is now empty
524 | for(translation, $$list($$__QDEP_ORIGINAL_TRANSLATIONS $$EXTRA_TRANSLATIONS)) {
525 | translation = $$basename(translation)
526 | QM_FILES += $$__qdep_lrelease_real_dir/$$replace(translation, \\\\..*$, .qm)
527 | }
528 | } else {
529 | # add qdep translations to extra to allow lrelease processing
530 | EXTRA_TRANSLATIONS += $$QDEP_TRANSLATIONS
531 | }
532 |
533 | # Create qdep pri export, if modules should be exported
534 | equals(TEMPLATE, lib):!qdep_no_link|qdep_link_export|qdep_export_all|!isEmpty(QDEP_EXPORTS): \\
535 | !qdepCreateExportPri($$QDEP_EXPORT_PATH/$$QDEP_EXPORT_NAME): \\
536 | error("Failed to create export file $$QDEP_EXPORT_FILE")
537 |
538 | DEFINES += $$QDEP_DEFINES
539 | INCLUDEPATH += $$QDEP_INCLUDEPATH
540 | LIBS += $$QDEP_LIBS
541 | """
542 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # qdep
2 | A very basic yet simple to use dependency management tool for qmake based projects.
3 |
4 | [](https://travis-ci.org/Skycoder42/qdep)
5 | [](https://ci.appveyor.com/project/Skycoder42/qdep/branch/master)
6 | [](https://www.codacy.com/app/Skycoder42/qdep)
7 | [](https://aur.archlinux.org/packages/qdep/)
8 | [](https://pypi.org/project/qdep/)
9 |
10 | ## Table of contents
11 | - [qdep](#qdep)
12 | * [Features](#features)
13 | * [Design Goals/Scope](#design-goals-scope)
14 | * [Installation](#installation)
15 | + [Preparing qmake](#preparing-qmake)
16 | + [Shell completitions](#shell-completitions)
17 | * [Getting started](#getting-started)
18 | * [Getting deeper](#getting-deeper)
19 | + [Dependency IDs](#dependency-ids)
20 | - [Versioning](#versioning)
21 | - [Package-uniqueness and version conflicts](#package-uniqueness-and-version-conflicts)
22 | + [Normal dependencies](#normal-dependencies)
23 | - [Translations](#translations)
24 | - [Library support](#library-support)
25 | - [Creating normal dependencies](#creating-normal-dependencies)
26 | * [Creating qdep translations](#creating-qdep-translations)
27 | * [Resources and hooks](#resources-and-hooks)
28 | * [Automatic exports](#automatic-exports)
29 | + [Project dependencies](#project-dependencies)
30 | - [Creating project dependencies](#creating-project-dependencies)
31 | * [Documentation](#documentation)
32 | + [Command line interface](#command-line-interface)
33 | - [Public API operations:](#public-api-operations-)
34 | - [Private API operations:](#private-api-operations-)
35 | + [QMAKE-Feature](#qmake-feature)
36 | - [Variables](#variables)
37 | * [Common Variables](#common-variables)
38 | * [SUBDIRS extension](#subdirs-extension)
39 | * [Advanced Variables](#advanced-variables)
40 | - [Configuration values](#configuration-values)
41 | * [Input value](#input-value)
42 | * [Output values](#output-values)
43 | * [Defines](#defines)
44 | - [Environment variables](#environment-variables)
45 | - [Public make targets](#public-make-targets)
46 | - [Internal](#internal)
47 | * [Variables](#variables-1)
48 | * [Configuration values](#configuration-values-1)
49 | * [QMAKE test functions](#qmake-test-functions)
50 | * [QMAKE replace functions](#qmake-replace-functions)
51 |
52 | Table of contents generated with markdown-toc
53 |
54 | ## Features
55 | - Seamless integration in qmake projects - no extra files needed
56 | - Basic dependency management using git repositories as package sources
57 | - Globally caches source files to speed up builds
58 | - Packages are simple pri-files that are included by the target project
59 | - Recursive dependency solving
60 | - Allows branch and tag based versioning
61 | - Supports translations for qdep packages
62 | - Supports automatic export of qdep packages from dynamic libraries
63 | - Handles QRC-Resources and startup hooks to work even when used in static libraries
64 | - Supports special "Project dependencies" wich allows you to add whole qmake projects to a SUBDIRS project
65 | - Can generate "library export" pri files that provide an easy and reliable way to link against libraries
66 | - Implicitly supports exported qdep packages and projects
67 |
68 | ## Design Goals/Scope
69 | qdep was designed with the following goals in mind:
70 |
71 | 1. 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 this
72 | 2. Simple 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 installation
73 | 3. Full Qt support: All features of Qt - including resources, startup hooks and translations should be supported with minimal effort for application developers
74 | 4. Library 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.
75 | 5. 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 needed
76 |
77 | Please 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.
78 |
79 | ## Installation
80 | To 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.
81 |
82 | 1. **Arch Linux:** Use the AUR-Package: [qdep](https://aur.archlinux.org/packages/qdep/)
83 | 2. **Any Platform:** Install via pip: [`pip install qdep`](https://pypi.org/project/qdep/)
84 | 3. **Any Platform:** Clone the repository and install the sources directly: `python setup.py install`
85 |
86 | ### Preparing qmake
87 | After 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:
88 | ```bash
89 | qdep prfgen --qmake ""
90 | ```
91 | 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:
92 | ```bash
93 | qdep prfgen --qmake "C:\Qt\5.12.0\msvc2017_64\bin\qmake.exe"
94 | ```
95 |
96 | **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/path` and export that very same path as value to the `QMAKEPATH` environment variable, if you have no such permissions.
97 |
98 | ### Shell completitions
99 | For Unix systems, qdep makes use of [argcomplete](https://argcomplete.readthedocs.io/en/latest/) to provide completitions for bash/zsh to activate them, add the following code your shell initializer scripts:
100 |
101 | For **zsh**, add this to `~/.zshrc`:
102 | ```bash
103 | autoload bashcompinit
104 | bashcompinit
105 | autoload compinit
106 | compinit
107 | eval "$(register-python-argcomplete qdep)"
108 | ```
109 |
110 | For **bash**, add this to `~/.bashrc`:
111 | ```
112 | eval "$(register-python-argcomplete qdep)"
113 | ```
114 |
115 | When using BASH, you can alternatively use global completition - see [Activating global completion](https://argcomplete.readthedocs.io/en/latest/#activating-global-completion) for 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 .
116 |
117 | ## Getting started
118 | The basic usage of qdep is very simple. For this example, we assume you want to add for example [QHotkey](https://github.com/Skycoder42/QHotkey) to 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:
119 |
120 | ```qmake
121 | QDEP_DEPENDS += Skycoder42/QHotkey
122 | !load(qdep):error("Failed to load qdep feature")
123 | ```
124 |
125 | 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 be `https://github.com/Skycoder42/QHotkey.git@1.2.2/qhotkey.pri`
126 |
127 | ## Getting deeper
128 | Besides 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.
129 |
130 | ### Dependency IDs
131 | Qdep dependencies are described by IDs. These IDs follow the format `[@[/]]`. The only relevant part is the URL, and it can either be implicit or explicit. Implicit URLs follow the format `/` and are automatically expanded to `https://github.com//.git`. For the explicit format, all kinds of GIT-URLs are supportet, i.e. HTTPS, SSH and FILE urls.
132 |
133 | If you leave out the path part of a qdep package, qdep assumes that there is a pri file named `.pri` in 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.
134 |
135 | #### Versioning
136 | Qdep 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.
137 |
138 | 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.
139 |
140 | #### Package-uniqueness and version conflicts
141 | When 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 `` and its `` - both case insensitive.
142 |
143 | ### Normal dependencies
144 | Normal dependencies, aka pri-dependencies specified via `QDEP_DEPENDS` are 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.
145 |
146 | #### Translations
147 | The first feature is extended support for translations. Qdep packages can come with translation source files for their own sources. These are typically exported via the `QDEP_TRANSLATIONS` qmake 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 the `lrelease` qmake feature, provided by qt. See [QMake TRANSLATIONS](https://doc.qt.io/qt-5/qmake-variable-reference.html#translations) and [QMake QM_FILES_INSTALL_PAT](https://doc.qt.io/qt-5/qmake-variable-reference.html#qm-files-install-path) for more details.
148 |
149 | #### Library support
150 | When 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:
151 | ```
152 | root (subdirs)
153 | |-library (lib)
154 | |-app (app)
155 | ```
156 | 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:
157 | ```qmake
158 | TEMPLATE = lib
159 | CONFIG += static # dynamic libraries are also possible, but dependencies must support exporting for them to work on windows
160 |
161 | # ...
162 |
163 | QDEP_DEPENDS += Skycoder42/QHotkey
164 | QDEP_EXPORTS += Skycoder42/QHotkey
165 | CONFIG += qdep_link_export
166 | !load(qdep):error("Failed to load qdep feature")
167 | ```
168 | And then reference the library in the app pro file. This also takes care of linking the library to the app, so no additional `INCLUDEPATH` or `LIBS` changes are needed:
169 | ```qmake
170 | TEMPLATE = app
171 |
172 | # ...
173 |
174 | QDEP_LINK_DEPENDS += ../library
175 | !load(qdep):error("Failed to load qdep feature")
176 | ```
177 | 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.
178 |
179 | **Note:** This will also work for dynamic librabries, but only if the *explicitly* support qdep package exporting. If not, linking will fail at least on windows, and possibly on other platforms as well.
180 |
181 | #### Creating normal dependencies
182 | This 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:
183 |
184 | - Translation generation
185 | - Resources and startup hooks
186 | - automatic exports
187 |
188 | ##### Creating qdep translations
189 | The 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:
190 | ```bash
191 | qdep lupdate --qmake "" --pri-file "" [-- ...]
192 | ```
193 | 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.
194 |
195 | 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:
196 | ```qmake
197 | !qdep_build: EXTRA_TRANSLATIONS += $$QDEP_TRANSLATIONS
198 | ```
199 |
200 | ##### Resources and hooks
201 | One 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.
202 |
203 | For hooks however, thats a different story. Assuming you have the following function you want to be run as `Q_COREAPP_STARTUP_FUNCTION`:
204 | ```cpp
205 | void my_package_startup_hook()
206 | {
207 | doStuff();
208 | }
209 | ```
210 | You will have to add the following line to your qdep pri file to make shure this hook actually gets called:
211 | ```pro
212 | QDEP_HOOK_FNS += my_package_startup_hook
213 | ```
214 | And with that, qdep will automatically generate the code needed to call this method as `Q_COREAPP_STARTUP_FUNCTION`.
215 |
216 | 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:
217 | ```cpp
218 | #ifndef QDEP_BUILD
219 | #include
220 | Q_COREAPP_STARTUP_FUNCTION(my_package_startup_hook)
221 | #endif
222 | ```
223 |
224 | ##### Automatic exports
225 | Another 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:
226 |
227 | ```cpp
228 | #ifdef BUILD_PACKAGE_AS_LIBRARY
229 | #ifdef IS_DLL_BUILD
230 | #define MY_PACKAGE_EXPORT Q_DECL_EXPORT
231 | #else
232 | #define MY_PACKAGE_EXPORT Q_DECL_IMPORT
233 | #endif
234 | #else
235 | #define MY_PACKAGE_EXPORT
236 | #endif
237 |
238 | class MY_PACKAGE_EXPORT MyClass {
239 | // ...
240 | };
241 | ```
242 |
243 | 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:
244 | ```cpp
245 | class MY_PACKAGE_EXPORT MyClass {
246 | // ...
247 | };
248 | ```
249 | To make it work, simply add the following to your pri file:
250 | ```qmake
251 | QDEP_PACKAGE_EXPORTS += MY_PACKAGE_EXPORT
252 | ```
253 | And 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).
254 |
255 | 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:
256 | ```qmake
257 | !qdep_build: DEFINES += "MY_PACKAGE_EXPORT="
258 | ```
259 |
260 | ### Project dependencies
261 | Another 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.
262 |
263 | To get started, assume the following project structure:
264 | ```
265 | root (subdirs)
266 | |--libs (subdirs)
267 | | |--lib (lib)
268 | |--app (app)
269 | ```
270 | Both lib and app are assumed to depend on a theoretical qdep project dependency name `Skycoder42/SuperLib`, but app also depends on lib.
271 |
272 | 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:
273 | ```qmake
274 | TEMPLATE = subdirs
275 | SUBDIRS += lib
276 |
277 | # ....
278 |
279 | QDEP_PROJECT_SUBDIRS += Skycoder42/SuperLib
280 | lib.qdep_depends += Skycoder42/SuperLib
281 | !load(qdep):error("Failed to load qdep feature")
282 | ```
283 | The `QDEP_PROJECT_SUBDIRS` is used to actually pull in the project dependency, while adding it to `lib.qdep_depends` *only* makes 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.
284 |
285 | 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:
286 | ```qmake
287 | QDEP_PROJECT_ROOT = libs # Or "./libs" - would be ".." for the lib project
288 | QDEP_PROJECT_LINK_DEPENDS += Skycoder42/SuperLib
289 | !load(qdep):error("Failed to load qdep feature")
290 | ```
291 | `QDEP_PROJECT_ROOT` tells qdep where the project is located, that contains the reference to the actual qdep project dependency, and `QDEP_PROJECT_LINK_DEPENDS` list all the dependencies this project (app) depends on. If any dependency listed there was not specified in the libs project via `QDEP_PROJECT_SUBDIRS`, the build will fail.
292 |
293 | 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.
294 |
295 | #### Creating project dependencies
296 | Generally speaking, project dependencies are just normal qmake projects. However, such a project should **always** include the qdep feature and add `qdep_link_export` to 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.
297 |
298 | 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 reference `Skycoder42/SuperLib` from within a qdep project dependency, add the following:
299 | ```qmake
300 | QDEP_PROJECT_DEPENDS += Skycoder42/SuperLib
301 | !load(qdep):error("Failed to load qdep feature")
302 | ```
303 |
304 | ## Documentation
305 | In the following sections, all the functions, variables etc. of qdep are documented for reference.
306 |
307 | ### Command line interface
308 | qdep 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, type `qdep --help`
309 |
310 | #### Public API operations:
311 | ```
312 | prfgen Generate a qmake project feature (prf) for the given qmake.
313 | init Initialize a pro file to use qdep by adding the required lines.
314 | lupdate Run lupdate for the QDEP_TRANSLATION variable in a given pri
315 | file.
316 | clear Remove all sources from the users global cache.
317 | versions List all known versions/tags of the given package
318 | query Query details about a given package identifier
319 | get Download the sources of one ore more packages into the source
320 | cache.
321 | update Check for newer versions of used packages and optionally update
322 | them.
323 | ```
324 |
325 | #### Private API operations:
326 | ```
327 | dephash Generated unique identifying hashes for qdep
328 | packages.
329 | pkgresolve Download the given qdep package and extract relevant
330 | information from it.
331 | hookgen Generate a header file with a method to load all
332 | resource hooks.
333 | hookimp Generate a source file that includes and runs all
334 | qdep hooks as normal startup hook.
335 | lconvert Combine ts files with translations from qdep
336 | packages.
337 | prolink Resolve the path a linked project dependency would
338 | be at.
339 | ```
340 |
341 | ### QMAKE-Feature
342 | This is the documentation of the qmake feature that is generated by qdep and loaded by adding `load(qdep)` to your project. All variables, CONFIG-flags and more are documented below.
343 |
344 | #### Variables
345 | ##### Common Variables
346 | Name | Direction | Default | Descriptions
347 | ----------------------------|-----------|-------------------------------|--------------
348 | QDEP_PATH | in/out | `` | Holds the path to the qdep binary to be used. Can be overwritten to specify a custom locations
349 | QDEP_VERSION | out | `` | The version of qdep which was used to generate the qdep feature
350 | QDEP_GENERATED_DIR | in | `$$OUT_PWD` | The sub-directory in the build folder where qdep should place all it's generated files. Can be an absolute or relative path
351 | QDEP_EXPORT_PATH | in | `$$QDEP_GENERATED_DIR/` | 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 path
352 | QDEP_DEPENDS | in | `` | Specify all dependencies to qdep packages to be included into your project
353 | QDEP_LINK_DEPENDS | in | `` | Reference other projects in the same build tree that this project should link against. Those projects must be exporting a pri file
354 | QDEP_PROJECT_SUBDIRS | in | `` | Specify all dependencies to qdep projects to be added as qmake project to the SUBDIRS variable. Only evaluted in projects that specify TEMPLATE as `subdirs`
355 | QDEP_PROJECT_ROOT | in | `` | The path to a project qmake project directory or file to resolve QDEP_PROJECT_LINK_DEPENDS against
356 | QDEP_PROJECT_LINK_DEPENDS | in | `` | 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.
357 | QDEP_DEFINES | in | `` | Specify DEFINES that are exported if the library exports a pri file. All values are automatically added to DEFINES by qdep
358 | QDEP_INCLUDEPATH | in | `` | Specify INCLUDEPATHS that are exported if the library exports a pri file. All values are automatically added to INCLUDEPATH by qdep
359 | QDEP_LIBS | in | `` | Specify LIBS that are exported if the library exports a pri file. All values are automatically added to LIBS by qdep
360 | QDEP_EXPORTS | in | `` | 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 this
361 | QDEP_LUPDATE_INPUTS | in | `` | Additional files or folders to parse for translations when running `make lupdate`
362 | QDEP_PACKAGE_EXPORTS | in (pkg) | `` | Variables to be defined as import/export/nothing and be used as prefix to a class.
363 | QDEP_TRANSLATIONS | in (pkg) | `` | Specify translation subfiles within a qdep dependency to be merged with the translations of the project they are included from
364 | QDEP_PROJECT_DEPENDS | in (pkg) | `` | 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 dependency
365 | QDEP_VAR_EXPORTS | in (pkg) | `` | Specify the names of additional qmake variables that should be exported from a qdep dependency besides DEFINES and INCLUDEPATH
366 |
367 | ##### SUBDIRS extension
368 | - `qdep_depends`: Can be added to any variable passed to `SUBDIRS` in a project that uses `QDEP_PROJECT_SUBDIRS` to specify that a certain subdir project depends on that specific package. This does **not** take care of linkage etc. It only ensures that the make targets are build in the correct order.
369 |
370 | ##### Advanced Variables
371 | Name | Direction | Default | Descriptions
372 | ----------------------------|-----------|------------------------------------------|--------------
373 | QDEP_TOOL | in/out | `` | The escaped command line base for qdep commands run from within qmake
374 | QDEP_CACHE_SCOPE | in | `stash` | The method of caching to be used to cache various qmake related stuff. Can be <empty>, super or stash
375 | QDEP_GENERATED_SOURCES_DIR | out | `$$QDEP_GENERATED_DIR/` | The directory where generated source files are placed. Is determined by the build configuration to take debug/release builds into account
376 | QDEP_GENERATED_TS_DIR | in | `$$QDEP_GENERATED_DIR/.qdepts/` | The directory where generated translation sources are placed.
377 | QDEP_LUPDATE | in | `lupdate -recursive -locations relative` | The path to the lupdate tool and additional arguments for lupdate in the `make lupdate` command to control it's behaviour
378 | QDEP_LCONVERT | in | `lconvert -sort-contexts` | The path to the lconvert tool and additional arguments to be used to combine translations
379 | QDEP_EXPORT_NAME | in/out | `_export.pri` | The name of a generated library import file. Must be only the name of the file, use QDEP_EXPORT_PATH to specify the location
380 | QDEP_EXPORTED_DEFINES | out | `` | DEFINES that come from QDEP_PACKAGE_EXPORTS or DEFINES from any directly included qdep dependency
381 | QDEP_EXPORTED_INCLUDEPATH | out | `` | INCLUDEPATHs that come from any directly included qdep dependency
382 | QDEP_EXPORTED_LIBS | out | `` | LIBS that come from any directly included qdep dependency
383 | QDEP_HOOK_FNS | out | `` | Holds all names of functions to be run as startup hook by qdep
384 |
385 | #### Configuration values
386 | ##### Input value
387 | - `qdep_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)
388 | - `qdep_no_pull`: Do not pull for newer versions of branch-based dependencies. Cloning new packages will still work
389 | - `qdep_no_clone`: Do not clone completely new packages or package versions. Pulling on already cached branches will still work
390 | - `qdep_no_cache`: Do not cache the used versions of packages without a specified versions. Instead the latest version is queried on every run
391 | - `qdep_export_all`: export all dependant packages, i.e. any QDEP_PACKAGE_EXPORTS for every package are treated as if added to QDEP_EXPORTS
392 | - `qdep_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 file
393 | - `qdep_no_qm_combine`: Do not combine TRANSLATIONS with QDEP_TRANSLATIONS. Instead treat QDEP_TRANSLATIONS as EXTRA_TRANSLATIONS and generate seperate qm files for them
394 | - `qdep_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 files
395 | - `qdep_no_export_link`: Do not export contents of QT, PKGCONFIG or QDEP_LIBS in a generated export pri file
396 |
397 | ##### Output values
398 | - `qdep_build`: Is set in case the qdep features was loaded correctly and is enabled
399 |
400 | ##### Defines
401 | - `QDEP_BUILD`: Is defined in case the qdep features was loaded correctly and is enabled
402 |
403 | #### Environment variables
404 | - `QDEP_CACHE_DIR`: The directory where to cache downloaded sources. Is automatically determined for every system but can be overwritten with this variable
405 | - `QDEP_SOURCE_OVERRIDE`: Allows to provide a mapping in the format `;^;`. This will make qdep automatically replace any occurance of `pkg1` with `pkg2` etc. Can be used by developers to temporarily overwrite packages
406 | - `QDEP_DEFAULT_PKG_FN`: A template that is used to resolve non-url packages like `User/package` to a full url. The default method for that is `https://github.com/{}.git` - with `{}` being replaced by the short package name.
407 |
408 | #### Public make targets
409 | - `make lupdate`: Runs the `lupdate` tool to update your translations in a quick and easy manner, without having to rely on potentially buggy pro-file-parsing.
410 |
411 | #### Internal
412 | ##### Variables
413 | - `__QDEP_PRIVATE_SEPERATOR`: Seperator between datasets
414 | - `__QDEP_TUPLE_SEPERATOR`: Seperator between values within a dataset
415 | - `__QDEP_INCLUDE_CACHE`: objects with details about included depdendencies of all kinds
416 | - `__QDEP_ORIGINAL_TRANSLATIONS`: All original translation that were in TRANSLATIONS before apply translation combination with QDEP_TRANSLATIONS from included packages
417 | - `__QDEP_HOOK_FILES`: Paths to header files that contain hook definitions that must be included and loaded by a project
418 | - `__QDEP_EXPORT_QMAKE_INCLUDE_GUARD`: a cache of included exported pri files to ensure each gets included only once
419 |
420 | ##### Configuration values
421 | - `__qdep_script_version`: The version detected at runtime as reported by the qdep executable
422 | - `__qdep_dump_dependencies`: Create a file named qdep_depends.txt in the build directory that contains all direct dependencies of the project
423 |
424 | ##### QMAKE test functions
425 | - `qdepCollectDependencies(dependencies ...)`: Downloads all specified normal dependencies and adds them to `__QDEP_INCLUDE_CACHE` to be linked later by qdep. Works recursively
426 | - `qdepCollectProjectDependencies(dependencies ...)`: Downloads all specified project dependencies and adds them to `SUBDIRS`. Works recursively
427 | - `qdepResolveSubdirDepends(subdir-vars ...)`: Converts the values of the `qdep_depends` subvar of all variables passed to the function to normal subdirs `depends` subvars
428 | - `qdepCreateExportPri(path)`: Creates a file named path and adds all export-related code to it
429 | - `qdepShellQuote(paths ...)`: Makes the given paths absolute based on `_PRO_FILE_PWD_` and escapes them using `shell_quote`
430 | - `qdepDumpUpdateDeps()`: Creates a file named qdep_depends.txt in the build directory that contains all direct dependencies of the project
431 |
432 | ##### QMAKE replace functions
433 | - `qdepResolveProjectLinkDeps(project-root, link-depends ...)`: Resolves all link-depends project packages to subdir paths, assuming they are provided by the project located at project-root
434 | - `qdepOutQuote(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 manner
435 | - `qdepLinkExpand(path)`: Expands a shortened or relative path to a project that exports dependencies to get the path of the export pri file
436 | - `qdepResolveLinkRoot(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
437 |
--------------------------------------------------------------------------------