Qmake에 대한 필수 가이드
게시 됨: 2022-03-11소개
qmake 는 여러 플랫폼에서 빌드 프로세스를 단순화하는 Qt 라이브러리와 함께 제공되는 빌드 시스템 도구입니다. CMake 및 Qbs 와 달리 qmake는 처음부터 Qt의 일부였으며 "기본" 도구로 간주됩니다. 말할 필요도 없이 Qt의 기본 IDE인 Qt Creator 는 기본적으로 qmake를 가장 잘 지원합니다. 예, 거기에서 새 프로젝트를 위해 CMake 및 Qbs 빌드 시스템을 선택할 수도 있지만 잘 통합되지 않습니다. Qt Creator의 CMake 지원은 시간이 지남에 따라 개선될 것이며, 이것이 특히 CMake를 대상으로 하는 이 가이드의 두 번째 판을 발행하는 좋은 이유가 될 것입니다. Qt Creator를 사용하지 않으려는 경우에도 공개 라이브러리나 플러그인을 구축하는 경우 qmake를 두 번째 구축 시스템으로 고려할 수 있습니다. 거의 모든 타사 Qt 기반 라이브러리 또는 플러그인은 qmake 기반 프로젝트에 원활하게 통합하는 데 사용되는 qmake 파일을 제공합니다. qmake 및 CMake와 같이 이중 구성을 제공하는 것은 몇 개뿐입니다. 다음 사항이 적용되는 경우 qmake 사용을 선호할 수 있습니다.
- 크로스 플랫폼 Qt 기반 프로젝트를 구축 중입니다.
- Qt Creator IDE 및 대부분의 기능을 사용 중입니다.
- 다른 qmake 프로젝트에서 사용할 독립형 라이브러리/플러그인을 구축 중입니다.
이 안내서는 가장 유용한 qmake 기능을 설명하고 각각에 대한 실제 예를 제공합니다. Qt를 처음 접하는 독자는 이 안내서를 Qt의 빌드 시스템에 대한 자습서로 사용할 수 있습니다. Qt 개발자는 새 프로젝트를 시작할 때 이것을 요리책으로 취급하거나 영향이 적은 기존 프로젝트에 일부 기능을 선택적으로 적용할 수 있습니다.
기본 Qmake 사용법
qmake 사양은 .pro
("프로젝트") 파일로 작성됩니다. 다음은 가장 간단한 .pro
파일의 예입니다.
SOURCES = hello.cpp
기본적으로 이것은 단일 소스 코드 파일 hello.cpp
에서 실행 파일을 빌드하는 Makefile
을 생성합니다.
바이너리(이 경우 실행 가능)를 빌드하려면 먼저 qmake를 실행하여 Makefile을 생성한 다음 make
(또는 도구 체인에 따라 nmake
또는 mingw32-make
)를 실행하여 대상을 빌드해야 합니다.
간단히 말해서 qmake 사양은 선택적 제어 흐름 명령문과 혼합된 변수 정의 목록에 불과합니다. 일반적으로 각 변수에는 문자열 목록이 있습니다. 제어 흐름 명령문을 사용하면 다른 qmake 사양 파일, 제어 조건 섹션 및 호출 기능을 포함할 수 있습니다.
변수 구문 이해하기
기존 qmake 프로젝트를 배울 때 \(VAR,\){VAR} 또는 $$(VAR) …
규칙을 적용하는 동안 이 미니 치트 시트를 사용하십시오.
-
VAR = value
에 값 할당 -
VAR += value
추가 -
VAR -= value
제거 -
$$VAR
또는$${VAR}
qmake가 실행될 때 VAR 값을 가져옵니다. -
$(VAR)
Makefile(qmake 아님)이 실행 중일 때 환경 VAR의 내용 -
$$(VAR)
qmake(Makefile 아님)가 실행 중일 때 환경 VAR의 내용
공통 템플릿
qmake 변수의 전체 목록은 사양에서 찾을 수 있습니다. http://doc.qt.io/qt-5/qmake-variable-reference.html
프로젝트에 대한 몇 가지 일반적인 템플릿을 검토해 보겠습니다.
# Windows application TEMPLATE = app CONFIG += windows # Shared library (.so or .dll) TEMPLATE = lib CONFIG += shared # Static library (.a or .lib) TEMPLATE = lib CONFIG += static # Console application TEMPLATE = app CONFIG += console
모든 소스 코드 파일을 나열하려면 SOURCES += ... 및 HEADERS += ... 를 추가하기만 하면 됩니다.
지금까지 아주 기본적인 템플릿을 살펴보았습니다. 더 복잡한 프로젝트에는 일반적으로 서로 종속된 여러 하위 프로젝트가 포함됩니다. qmake로 이것을 어떻게 관리하는지 봅시다.
하위 프로젝트
가장 일반적인 사용 사례는 하나 이상의 라이브러리 및 테스트 프로젝트와 함께 제공되는 애플리케이션입니다. 다음 구조를 고려하십시오.
/project ../library ..../include ../library-tests ../application
분명히 우리는 다음과 같이 한 번에 모든 것을 구축할 수 있기를 원합니다.
cd project qmake && make
이 목표를 달성하려면 /project
폴더 아래에 qmake 프로젝트 파일이 필요합니다.
TEMPLATE = subdirs SUBDIRS = library library-tests application library-tests.depends = library application.depends = library
참고: CONFIG += ordered
을 사용하는 것은 나쁜 습관으로 간주됩니다. 대신 .depends
를 사용하는 것이 좋습니다.
이 사양은 다른 대상이 종속되어 있기 때문에 qmake가 라이브러리 하위 프로젝트를 먼저 빌드하도록 지시합니다. 그런 다음 이 둘은 종속적이기 때문에 임의의 순서로 library-tests
와 애플리케이션을 빌드할 수 있습니다.
라이브러리 연결
위의 예에는 응용 프로그램에 연결해야 하는 라이브러리가 있습니다. C/C++에서는 다음과 같이 몇 가지를 더 구성해야 합니다.
- #include 지시문에 대한 검색 경로를 제공하려면
-I
를 지정합니다. -
-L
을 지정하여 링커에 대한 검색 경로를 제공합니다. -
-l
을 지정하여 링크해야 하는 라이브러리를 제공합니다.
모든 하위 프로젝트를 이동할 수 있기를 원하기 때문에 절대 또는 상대 경로를 사용할 수 없습니다. 예를 들어 INCLUDEPATH += ../library/include 와 같이 하면 안 되며 물론 임시 빌드 폴더에서 라이브러리 바이너리(.a 파일)를 참조할 수 없습니다. "관심 분리" 원칙에 따라 응용 프로그램 프로젝트 파일이 라이브러리 세부 정보에서 추상화되어야 함을 빠르게 인식할 수 있습니다. 대신 헤더 파일 등을 찾을 위치를 알려주는 것은 라이브러리의 책임입니다.
이 문제를 해결하기 위해 qmake의 include()
지시문을 활용합시다. 라이브러리 프로젝트에서 확장자가 .pri
인 새 파일에 다른 qmake 사양을 추가할 것입니다(확장자는 무엇이든 될 수 있지만 여기서 i
는 포함을 나타냄). 따라서 라이브러리에는 library.pro
및 library.pri
의 두 가지 사양이 있습니다. 첫 번째는 라이브러리를 빌드하는 데 사용되며 두 번째는 소비 프로젝트에 필요한 모든 세부 정보를 제공하는 데 사용됩니다.
library.pri 파일의 내용은 다음과 같습니다.
LIBTARGET = library BASEDIR = $${PWD} INCLUDEPATH *= $${BASEDIR}/include LIBS += -L$${DESTDIR} -llibrary
BASEDIR
은 라이브러리 프로젝트의 폴더를 지정합니다(정확히 말하면 현재 qmake 사양 파일의 위치, 이 경우에는 library.pri
임). 짐작할 수 있듯이 INCLUDEPATH
는 /project/library/include
로 평가됩니다. DESTDIR
은 빌드 시스템이 (.o .a .so .dll 또는 .exe 파일)과 같은 출력 아티팩트를 배치하는 디렉토리입니다. 이것은 일반적으로 IDE에서 구성되므로 출력 파일이 있는 위치에 대해 어떤 가정도 해서는 안 됩니다.
application.pro
파일에 include(../library/library.pri)
를 추가하기만 하면 끝입니다.
이 경우 애플리케이션 프로젝트가 어떻게 구축되는지 검토해 보겠습니다.
- 최상위
project.pro
는 하위 디렉토리 프로젝트입니다. 라이브러리 프로젝트를 먼저 빌드해야 한다고 알려줍니다. 따라서 qmake는 라이브러리의 폴더에 들어가library.pro
를 사용하여 빌드합니다. 이 단계에서library.a
가 생성되어DESTDIR
폴더에 배치됩니다. - 그런 다음 qmake는 응용 프로그램 하위 폴더에 들어가
application.pro
파일을 구문 분석합니다. qmake가 즉시 읽고 해석하도록 지시하는include(../library/library.pri)
지시문을 찾습니다. 이것은INCLUDEPATH
및LIBS
변수에 새로운 정의를 추가하므로 이제 컴파일러와 링커는 포함 파일, 라이브러리 바이너리 및 연결할 라이브러리를 검색할 위치를 알 수 있습니다.
라이브러리 테스트 프로젝트의 빌드를 건너뛰었지만 응용 프로그램 프로젝트와 동일합니다. 분명히 우리의 테스트 프로젝트는 테스트해야 하는 라이브러리를 링크해야 합니다.

이 설정을 사용하면 라이브러리 프로젝트를 다른 qmake 프로젝트로 쉽게 이동하고 포함할 수 있으므로 .pri
파일을 참조할 수 있습니다. 이것이 바로 커뮤니티에서 타사 라이브러리를 배포하는 방식입니다.
config.pri
복잡한 프로젝트에는 많은 하위 프로젝트에서 사용되는 일부 공유 구성 매개변수가 있는 것이 매우 일반적입니다. 중복을 피하기 위해 다시 include()
지시문을 활용하고 최상위 폴더에 config.pri
를 생성할 수 있습니다. 또한 이 가이드에서 다음에 논의하는 것과 유사하게 하위 프로젝트에 공유되는 공통 qmake "유틸리티"가 있을 수 있습니다.
DESTDIR에 아티팩트 복사
종종 프로젝트에는 라이브러리 또는 응용 프로그램과 함께 배포해야 하는 "기타" 파일이 있습니다. 빌드 프로세스 중에 이러한 모든 파일을 DESTDIR
에 복사할 수만 있으면 됩니다. 다음 스니펫을 고려하십시오.
defineTest(copyToDestDir) { files = $$1 for(FILE, files) { DDIR = $$DESTDIR FILE = $$absolute_path($$FILE) # Replace slashes in paths with backslashes for Windows win32:FILE ~= s,/,\\,g win32:DDIR ~= s,/,\\,g QMAKE_POST_LINK += $$QMAKE_COPY $$quote($$FILE) $$quote($$DDIR) $$escape_expand(\\n\\t) } export(QMAKE_POST_LINK) }
참고: 이 패턴을 사용하여 파일에서 작동하는 재사용 가능한 함수를 정의할 수 있습니다.
이 코드를 /project/copyToDestDir.pri
에 배치하면 다음과 같이 까다로운 하위 프로젝트에 include()
할 수 있습니다.
include(../copyToDestDir.pri) MYFILES += \ parameters.conf \ testdata.db ## this is copying all files listed in MYFILES variable copyToDestDir($$MYFILES) ## this is copying a single file, a required DLL in this example copyToDestDir($${3RDPARTY}/openssl/bin/crypto.dll)
참고: DISTFILES는 같은 목적으로 도입되었지만 Unix에서만 작동합니다.
코드 생성
사전 빌드 단계로 코드 생성의 좋은 예는 C++ 프로젝트에서 Google protobuf를 사용하는 경우입니다. protoc
실행을 빌드 프로세스에 주입하는 방법을 살펴보겠습니다.
적합한 솔루션을 쉽게 Google에 검색할 수 있지만 한 가지 중요한 코너 케이스를 알고 있어야 합니다. A가 B를 참조하는 두 개의 계약이 있다고 상상해보십시오.
A.proto <= B.proto
A.proto
에 대한 코드를 먼저 생성하고( A.pb.h
및 A.pb.cxx
생성) 이를 컴파일러에 공급하면 종속성 B.pb.h
가 아직 존재하지 않기 때문에 실패합니다. 이를 해결하려면 결과 소스 코드를 빌드하기 전에 모든 프로토 코드 생성 단계를 통과해야 합니다.
https://github.com/jmesmon/qmake-protobuf-example/blob/master/protobuf.pri에서 이 작업에 대한 훌륭한 스니펫을 찾았습니다.
상당히 큰 스크립트이지만 사용 방법을 이미 알고 있어야 합니다.
PROTOS = A.proto B.proto include(protobuf.pri)
protobuf.pri
를 살펴보면 모든 사용자 지정 컴파일 또는 코드 생성에 쉽게 적용할 수 있는 일반 패턴을 확인할 수 있습니다.
my_custom_compiler.name = my custom compiler name my_custom_compiler.input = input variable (list) my_custom_compiler.output = output file path + pattern my_custom_compiler.commands = custom compilation command my_custom_compiler.variable_out = output variable (list) QMAKE_EXTRA_COMPILERS += my_custom_compiler
범위 및 조건
종종 Windows 또는 MacOS와 같은 특정 플랫폼에 대한 선언을 정의해야 합니다. Qmake는 win32, macx 및 unix의 세 가지 사전 정의된 플랫폼 표시기를 제공합니다. 구문은 다음과 같습니다.
win32 { # add Windows application icon, not applicable to unix/macx platform RC_ICONS += icon.ico }
범위는 중첩될 수 있으며 연산자를 사용할 수 있습니다 !
, |
와일드 카드도 있습니다.
macx:debug { # include only on Mac and only for debug build HEADERS += debugging.h } win32|macx { HEADERS += windows_or_macx.h } win32-msvc* { # same as win32-msvc|win32-mscv.net }
참고: Unix는 Mac OS에서 정의됩니다! Mac OS(일반 Unix가 아님)를 테스트하려면 unix:!macx
조건을 사용하십시오.
Qt Creator에서 범위 조건 debug
및 release
가 예상대로 작동하지 않습니다. 이러한 작업을 제대로 수행하려면 다음 패턴을 사용하세요.
CONFIG(debug, debug|release) { LIBS += ... } CONFIG(release, debug|release) { LIBS += ... }
유용한 기능
Qmake에는 자동화를 추가하는 여러 내장 기능이 있습니다.
첫 번째 예는 files()
함수입니다. 다양한 수의 소스 파일을 생성하는 코드 생성 단계가 있다고 가정합니다. SOURCES
에 모두 포함하는 방법은 다음과 같습니다.
SOURCES += $$files(generated/*.c)
generated
된 하위 폴더에서 확장자가 .c
인 모든 파일을 찾아 SOURCES
변수에 추가합니다.
두 번째 예제는 이전 예제와 유사하지만 이제 코드 생성에서 출력 파일 이름(파일 목록)이 포함된 텍스트 파일이 생성되었습니다.
SOURCES += $$cat(generated/filelist, lines)
이것은 파일 내용을 읽고 각 행을 SOURCES
에 대한 항목으로 취급합니다.
참고: 포함된 기능의 전체 목록은 http://doc.qt.io/qt-5/qmake-function-reference.html에서 찾을 수 있습니다.
경고를 오류로 처리
다음 스니펫은 앞에서 설명한 조건부 범위 기능을 사용합니다.
*g++*: QMAKE_CXXFLAGS += -Werror *msvc*: QMAKE_CXXFLAGS += /WX
이 문제가 발생하는 이유는 MSVC에 이 옵션을 활성화하는 다른 플래그가 있기 때문입니다.
Git 버전 생성
다음 스니펫은 Git에서 얻은 현재 SW 버전을 포함하는 전처리기 정의를 생성해야 할 때 유용합니다.
DEFINES += SW_VERSION=\\\"$$system(git describe --always --abbrev=0)\\\"
이것은 git
명령을 사용할 수 있는 한 모든 플랫폼에서 작동합니다. Git 태그를 사용하는 경우 분기가 진행되었더라도 가장 최근 태그를 엿볼 것입니다. git describe
명령을 수정하여 원하는 출력을 얻습니다.
결론
Qmake는 크로스 플랫폼 Qt 기반 프로젝트 구축에 중점을 둔 훌륭한 도구입니다. 이 가이드에서는 프로젝트 구조를 유연하게 유지하고 사양을 쉽게 읽고 유지 관리할 수 있도록 기본 도구 사용법과 가장 일반적으로 사용되는 패턴을 검토했습니다.
Qt 앱을 더 멋지게 만드는 방법을 배우고 싶으십니까? 시도: 베지어 곡선과 QPainter를 사용하여 C++에서 둥근 모서리 모양을 얻는 방법: 단계별 가이드