I try to make my Qt Installer project more comfortable.
I need to centralize information about my application and components (config.xml and package.xml) in one file. I don't want to jump on different files with same name and search for changeable elements between xml tags.
My first thougt is doing it right in *.pro file of installer project. I place sections of variables in header of installer project file. But where I need to place the code for xml generating?
What is the better (native / comfortable / crossplatform) way to do this?
The answer is simple here: you cannot generate XML files for Qt Installer: you write them manually, as explained in the documentation.
This section describes the following tasks that you must accomplish to create the installer:
Create a package directory that will contain all the configuration files and installable packages.
Create a configuration file that contains information about how to build the installer binaries and online repositories.
Create a package information file that contains information about the installable components.
Create installer content and copy it to the package directory.
Use the binarycreator tool to create the installer.
However, if you look closer at the examples, you can still generate the installer in the *.pro file. Let's pick an example randomly, System Info:
TEMPLATE = aux
INSTALLER = installer
INPUT = $$PWD/config/config.xml $$PWD/packages
example.input = INPUT
example.output = $$INSTALLER
example.commands = ../../bin/binarycreator -c $$PWD/config/config.xml -p $$PWD/packages ${QMAKE_FILE_OUT}
example.CONFIG += target_predeps no_link combine
QMAKE_EXTRA_COMPILERS += example
OTHER_FILES = README
If you want to apply this to your project, I think you'll have to modify the ../../bin/binarycreator line and make it system aware, by changing your PATH. It might be possible to call an external script and parse XML files, and make the substitutions you would like to do, but you'd move the complexity to another place.
Instead of maintaining plain good old XML files, you would be creating something between XSLT and XML. Maybe you could just write XSLT (or XSL or XQUERY) and generate XML but I don't know anyone who is using it anymore. Last time I used it was when I was learning Computer Science a long time ago :)
This is possible using the QMAKE_SUBSTITUTES feature which will substitute qmake variables into the given input files and put the output in the build folder.
This runs at qmake time rather than at build time. If this is suitable then you just need to add a target to copy the generated files from the build dir to your source dir.
If you need it to run at build time then you can create a .pri file containing QMAKE_SUBSTITUTES and a target in the main .pro file that will run qmake on this file during the build process.
Main .pro file:
create_xml.commands += $(QMAKE) $$shell_quote($$PWD/config/generate_xml.pri) $$escape_expand(\n\t)
create_xml.commands += $(COPY) $$shell_quote($${OUT_PWD}/config.xml) $$shell_quote($$PWD/config) $$escape_expand(\n\t)
create_xml.commands += $(COPY) $$shell_quote($${OUT_PWD}/package.xml) $$shell_quote($$PWD/packages/my.app.id/meta) $$escape_expand(\n\t)
create_xml.depends = $$PWD/version.pri
QMAKE_EXTRA_TARGETS += create_xml
generate_xml.pri:
TEMPLATE = aux
message("Generating $$OUT_PWD/config.xml and $$OUT_PWD/package.xml")
# Get the version number
include(version.pri)
APP_VERSION = $$VERSION
QMAKE_SUBSTITUTES += package.xml.in config.xml.in
config.xml.in: Note that you need to escape the quotes.
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<Installer>
<Name>MyApp</Name>
<Version>$$APP_VERSION</Version>
...
Related
I'm working on a GTK3 application, but using QTCreator as my IDE, just because I happen to like it. It works fine, I can easily turn off all the QT-specific stuff and link the GTK libraries. There's just one little issue.
GTK uses XML files to define its resources. It comes with a program, "glib-compile-resources", which takes a .gresource.xml file and produces a .c file*, which can then be included in your project. The problem is that QTCreator doesn't know about glib-compile-resources, so I have to remember to run the program manually every time I make any change to them.
I've tried using a custom build step, but if I do that, then QT rebuilds the file every time, even if it hasn't changed, which slows the process down. In addition, if the C file doesn't already exist, it will fail with a "No rule to make target 'x.c' needed by 'x.o'. Stop." error, so I have to run the program manually anyway.
Is there any way to tell QTCreator to run glib-compile-resources whenever it encounters a .gresource.xml file, and include the resulting C file into the final compilation?
*There are other options available then just a straight C source file, but C source is the simplest and easiest for me.
You can add a custom target in your qmake file (rather than in your QtCreator project config). See the qmake docs at https://doc.qt.io/qt-5/qmake-advanced-usage.html#adding-custom-targets and/or https://doc.qt.io/qt-5/qmake-advanced-usage.html#adding-compilers.
Update: this is a simple example which shows how to do this for a single file, using the custom target mechanism in your .pro file:
glib_resources.target = glib-resources.c
glib_resources.depends = glib-resources.xml
glib_resources.commands = glib-compile-resources --target $$glib_resources.target --generate-source $$glib_resources.depends
QMAKE_EXTRA_TARGETS += glib_resources
PRE_TARGETDEPS += glib-resources.c ## set this target as a dependency for the actual build
QMAKE_CLEAN += glib-resources.c ## delete the file at make clean
Here's how I wound up solving my own issue:
I found in the QT documentation how to add your own custom compiler to a QT project file. The exact lines needed are:
GLIB_RESOURCE_FILES += \
resources.gresource.xml
# Add more resource files here.
glib_resources.name = glibresources
glib_resources.input = GLIB_RESOURCE_FILES
glib_resources.output = ${QMAKE_FILE_IN_BASE}.c
glib_resources.depend_command = glib-compile-resources --generate-dependencies ${QMAKE_FILE_IN}
glib_resources.commands = glib-compile-resources --target ${QMAKE_FILE_OUT} --sourcedir ${QMAKE_FILE_IN_PATH} --generate-source ${QMAKE_FILE_IN}
glib_resources.variable_out = SOURCES
glib_resources.clean = ${QMAKE_FILE_OUT}
QMAKE_EXTRA_COMPILERS += glib_resources
(Thanks to #zgyarmati, who's post lead me to the right answer.)
Whenever I set my build options in Qt for a specific folder and I compile using MVSC it creates a release and debug folder and puts the output exe file inside that folder. If I compile in linux it usually just puts the final executable file in the folder that I specify. Is there a way to get this last behaviour (that is to stop the creation release and debug folder)?
You can set CONFIG -= debug_and_release in your .pro file and it will stop doing so.
With qmake you can actually specify a destination directory for your binary(ies), and other generated output as well. For example:
DESTDIR = $${OUT_PWD}/bin # this is where the binaries ('target' files) go
OBJECTS_DIR = $${OUT_PWD}/obj # compiled objects
MOC_DIR = $${OUT_PWD}/moc # generated MOC files
UI_DIR = $${OUT_PWD}/ui # generated C++ code from .ui files
RCC_DIR = $${OUT_PWD}/rcc # generated C++ code from .qrc files
OUT_PWD is a built-in variable specifying the current build directory. You could actually use any valid path here.
Reference: http://doc.qt.io/qt-5/qmake-variable-reference.html
This has been on my mind for years. It is about using resource files with CMake in code such that they can be accessed when running in the build directory and when installed.
Say you have files necessary to run your program in a top-level directory called res. It contains res/file1.txt and res/file2.txt. You build this program and the resources are copied to the build directory from which the program can be run during development. Then, the program is installed with the resources into /usr/share/my_program or something and the program runs by accessing those files. The question is what to do in the source code so that the same code works when running from the build directory and when installed. I've seen several different answers for how to handle resources but each seems to have a flaw:
The program searches in some preconfigured absolute directory such as CMAKE_INSTALL_PREFIX/share/my_program and must be installed first to run, not ideal for easy use during development.
Finding some way to use relative paths to the binary but I don't see how this would work since the build tree will not mirror the installed file paths, being installed to the bin and share directories.
Differentiating between the two with a CMake variable so that it searches for a relative path in one scenario or the installed location with another. This could possibly just be the debug/release variable. It would also require rerunning CMake before installing to rebuild with the new resource paths.
Baking the files into the executable. Seems uneccessary when dealing with most resources since they could just be opened instead and may be inconvenient with large directories of files.
Is one of these the best solution? Is there something I'm not misunderstanding? I've always been under the impression programs should be able to be run from the build directory before installing to see if they work. If possible, I would like to know what both the CMake and C/C++ code would look like, such as open("my_resource_location"); and my_cmake_command(). Most answers I've seen relating to resources don't include both.
It seems to me that what you are looking for is a relocatable build.
One way to achieve this in CMake is to use configuration files to incorporate resource paths in your code. I have prepared a minimal and reproducible example to give you an idea of how it works.
First, imagine you have the following program structure on disk:
main
CMakeLists.txt
main.cpp
data
CMakeLists.txt
Resources.h.in (Thats the configuration file)
resource.txt
The resource.txt file only contains a string. The program simply opens that file, reads its content and displays it to the terminal. So you have something like:
#include <fstream>
#include <iostream>
#include <string>
#include <Resources.h>
const std::string MY_FILE = "/resource.txt";
int main(int argc, char *argv[])
{
std::string line;
std::ifstream myfile (RESOURCE_PATH + MY_FILE);
if(myfile.is_open())
{
while(std::getline(myfile,line))
{
std::cout << line << '\n';
}
myfile.close();
}
else
{
std::cout << "Unable to open file";
}
return 0;
}
Note that this file #includes a Resources.h file, but that this file does not exist in our project. However, we have a Resources.h.in configuration file that CMake will turn into a Resources.h file on generation. This generated file will then contain the resource path we need inside the RESOURCE_PATH variable. Here is what the configuration file contains:
#define RESSOURCE_PATH "#CMAKE_INSTALL_PREFIX#/#CMAKE_INSTALL_DATADIR#"
A simple #define with this strange #something# notation. On generation, CMake will make the appropriate substitution and write the result to Resource.h, which we can then consume. The main CMakeLists.txt file is pretty straight forward:
cmake_minimum_required(VERSION 3.10)
project(example)
# Sets up classic installation directories. However,
# you can override them:
include(GNUInstallDirs)
add_subdirectory(data)
add_subdirectory(main)
I use the GNUInstallDirs, which populates variables such as CMAKE_INSTALL_DATADIR, for convinience. The main/CMakeLists.txt file:
add_executable(example main.cpp)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Ressources.h.in" "Ressources.h")
target_include_directories(example
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
install(
TARGETS example
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
This is where the configuration file is set up. The target_include_directories just below is added for CMake to be able to find the generated Resource.h file, which will reside in the build directory. Finally, the data/CMakeLists.txt file:
install(
FILES resource.txt
DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}
)
only has a simple installation instruction. From there, you can install as usual. Simply create a build directory and use CMake as usual. For example, if the build directory is located at the same level as the project:
mkdir ../build
cd ../build
cmake ../project
make
sudo make install
Since by default (on Linux) we have:
CMAKE_INSTALL_PREFIX=/usr/local
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share
The program will be installed to /usr/local/bin and the resource to /usr/local/share. Note that the Resource.h file will give you the correct path once generated by CMake. Whats is nice now, however, is that you can modify the value of CMAKE_INSTALL_PREFIX to point to somewhere else by calling CMake like so (for example, this could be a dev build directory):
cmake -DCMAKE_INSTALL_PREFIX=/home/someone/example ../project
and you will have these values instead:
CMAKE_INSTALL_PREFIX=/home/someone/example
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share
The Resources.h file will also be updated to point to the right location, and your resource will still be found.
Is there a way to link with a library that's not in the current package path.
This link suggests placing everything under the local directory. Our packages are installed in some repository elsewhere. I just want to specify the libpath to it on windows.
authors = ["Me"]
links = "CDbax"
[target.x86_64-pc-windows-gnu.CDbax]
rustc-link-lib = ["CDbax"]
rustc-link-search = ["Z:/Somepath//CPP/CDbax/x64/Debug/"]
root = "Z:/Somepath//CPP/CDbax/x64/Debug/"
But trying cargo build -v gives me
package `hello v0.1.0 (file:///H:/Users/Mushfaque.Cradle/Documents/Rustc/hello)` specifies that it links to `CDbax` but does not have a custom build script
From the cargo build script support guide, it seems to suggest that this should work. But I can see that it hasn't added the path. Moving the lib into the local bin\x68_64-pc-windows-gnu\ path works however.
Update
Thanks to the answer below, I thought I'd update this to give the final results of what worked on my machine so others find it useful.
In the Cargo.toml add
links = "CDbax"
build = "build.rs"
Even though there is no build.rs file, it seems to require it (?) otherwise complains with
package `xxx v0.1.0` specifies that it links to `CDbax` but does not have a custom build script
Followed by Vaelden answer's create a 'config' file in .cargo
If this is a sub crate, you don't need to put the links= tag in the parent crate, even though it's a dll; even with a 'cargo run'. I assume it adds the dll path to the execution environment
I think the issue is that you are mistaking the manifest of your project with the cargo
configuration.
The manifest is the Cargo.toml file at the root of your project. It describes your project itself.
The cargo configuration describes particular settings for cargo, and allow for example to override dependencies, or in your case override build scripts. The cargo configuration files have a hierarchical structure:
Cargo allows to have local configuration for a particular project or
global configuration (like git). Cargo also extends this ability to a
hierarchical strategy. If, for example, cargo were invoked in
/home/foo/bar/baz, then the following configuration files would be
probed for:
/home/foo/bar/baz/.cargo/config
/home/foo/bar/.cargo/config
/home/foo/.cargo/config
/home/.cargo/config
/.cargo/config
With this structure you can specify local configuration per-project,
and even possibly check it into version control. You can also specify
personal default with a configuration file in your home directory.
So if you move the relevant part:
[target.x86_64-pc-windows-gnu.CDbax]
rustc-link-lib = ["CDbax"]
rustc-link-search = ["Z:/Somepath//CPP/CDbax/x64/Debug/"]
root = "Z:/Somepath//CPP/CDbax/x64/Debug/"
to any correct location for a cargo configuration file, it should work.
Is there a way to find out the project path at compile time?
I want to create a unit test that tests if the configurartion in the default web.config (the one in the project folder). Mainly to reduce human error.
I cannot rely on assembly locations at runtime (for the test), so I need to know where the project folder is to access web.config there.
I need a "generic" solution since I'd like to use the same (base) test code for multiple projects, and the physical location is different anyway for most development machines.
Thanks.
Based on rkb's answer,
As it sounds like you've got a C# project, use this post build step.
echo namespace ProjectPath { static public class ProjectPath { public static readonly string Path = #"$(ProjectDir)";} } > $(ProjectDir)path.cs
Then include path.cs as an existing item to your test project. Then you can access it via:
string path = ProjectPath.ProjectPath.Path;
If you want the Visual Studio project path, at compile time, you could use a Pre-Build Event (see the Project Properties dialog) to run a command line that will create a source file used in your project.
The source file will contain some code, say a variable definition. Your testing code uses this variable. The value of the variable will come from VS; when it runs your Pre-Build Event command, it substitutes project properties for certain macros. The macro you want is probably ProjectDir.
So in the end, you have something like this for your Pre-Build Event's command:
echo 'const char * PROJECT_PATH = "$(ProjectDir)";' > source.cpp
Not sure what language you're using, so adjust accordingly.
To improve the solution slightly, instead of using the Post Build Event Command Line, you can run the command as an MSbuild Exec Task in the BeforeBuild Target of the project.