Skip to content

Commit

Permalink
CMake: pro2cmake.py: Add basic support for examples
Browse files Browse the repository at this point in the history
Examples need to be built stand-alone and as part of Qt, so they
need a special CMakeLists.txt file that supports both use-cases.

Add an --is-example switch to pro2cmake to make it generate these special
CMakeLists.txt files.

This is basic support only and is currently still missing the necessary
find_package calls.

Change-Id: Ie770287350fb8a41e872cb0ea607923caa33073d
Reviewed-by: Alexandru Croitor <[email protected]>
  • Loading branch information
hunger committed May 9, 2019
1 parent 5c98110 commit 5608bf3
Showing 1 changed file with 118 additions and 39 deletions.
157 changes: 118 additions & 39 deletions util/cmake/pro2cmake.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ def _parse_commandline():
dest='debug_full_pro_structure', action='store_true',
help='Dump the full structure of the qmake .pro-file '
'(with includes).')

parser.add_argument('--example', action='store_true',
dest="is_example",
help='Treat the input .pro file as an example.')

parser.add_argument('files', metavar='<.pro/.pri file>', type=str,
nargs='+', help='The .pro/.pri file to process')

Expand Down Expand Up @@ -826,7 +831,7 @@ def map_condition(condition: str) -> str:


def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *,
indent: int = 0) -> None:
indent: int = 0, is_example: bool=False) -> None:
ind = ' ' * indent
for sd in scope.get_files('SUBDIRS'):
if os.path.isdir(sd):
Expand All @@ -839,7 +844,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *,
'', scope.basedir)

do_include(subdir_scope)
cmakeify_scope(subdir_scope, cm_fh, indent=indent)
cmakeify_scope(subdir_scope, cm_fh, indent=indent, is_example=is_example)
elif sd.startswith('-'):
cm_fh.write('{}### remove_subdirectory'
'("{}")\n'.format(ind, sd[1:]))
Expand All @@ -853,7 +858,7 @@ def handle_subdir(scope: Scope, cm_fh: typing.IO[str], *,
elif cond:
cm_fh.write('\n{}if({})\n'.format(ind, cond))

handle_subdir(c, cm_fh, indent=indent + 1)
handle_subdir(c, cm_fh, indent=indent + 1, is_example=is_example)

if cond:
cm_fh.write('{}endif()\n'.format(ind))
Expand Down Expand Up @@ -914,10 +919,10 @@ def write_source_file_list(cm_fh: typing.IO[str], scope, cmake_parameter: str,
cm_fh.write(header)
extra_indent = ''
if cmake_parameter:
cm_fh.write('{} {}\n'.format(ind, cmake_parameter))
cm_fh.write('{}{}\n'.format(ind, cmake_parameter))
extra_indent = ' '
for s in sort_sources(sources):
cm_fh.write('{} {}{}\n'.format(ind, extra_indent, s))
cm_fh.write('{}{}{}\n'.format(ind, extra_indent, s))
cm_fh.write(footer)


Expand Down Expand Up @@ -951,6 +956,54 @@ def write_library_list(cm_fh: typing.IO[str], cmake_keyword: str,
cm_fh.write('{} {}\n'.format(ind, d))


def write_all_source_file_lists(cm_fh: typing.IO[str], scope: Scope, header: str, *,
indent: int = 0, footer: str = ''):
write_source_file_list(cm_fh, scope, header,
['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'],
indent)


def write_defines(cm_fh: typing.IO[str], scope: Scope, header: str, *,
indent: int = 0, footer: str = ''):
ind = spaces(indent)

defines = scope.expand('DEFINES')
defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')]
if defines:
cm_fh.write('{}{}\n'.format(ind, header))
for d in defines:
d = d.replace('=\\\\\\"$$PWD/\\\\\\"',
'="${CMAKE_CURRENT_SOURCE_DIR}/"')
cm_fh.write('{} {}\n'.format(ind, d))
if footer:
cm_fh.write('{}{}\n'.format(ind, footer))

def write_include_paths(cm_fh: typing.IO[str], scope: Scope, header: str, *,
indent: int = 0, footer: str = ''):
ind = spaces(indent)

includes = scope.get_files('INCLUDEPATH')
if includes:
cm_fh.write('{}{}\n'.format(ind, header))
for i in includes:
i = i.rstrip('/') or ('/')
cm_fh.write('{} {}\n'.format(ind, i))
if footer:
cm_fh.write('{}{}\n'.format(ind, footer))


def write_compile_options(cm_fh: typing.IO[str], scope: Scope, header: str, *,
indent: int = 0, footer: str = ''):
ind = spaces(indent)

compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')]
if compile_options:
cm_fh.write('{}{}\n'.format(ind, header))
for co in compile_options:
cm_fh.write('{} "{}"\n'.format(ind, co))
if footer:
cm_fh.write('{}{}\n'.format(ind, footer))


def write_library_section(cm_fh: typing.IO[str], scope: Scope,
public: typing.List[str],
Expand Down Expand Up @@ -1001,37 +1054,23 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *,
if plugin_type:
cm_fh.write('{} TYPE {}\n'.format(ind, plugin_type))

source_keys: typing.List[str] = []
write_source_file_list(cm_fh, scope, 'SOURCES',
['SOURCES', 'HEADERS', 'OBJECTIVE_SOURCES', 'NO_PCH_SOURCES', 'FORMS'],
indent)
write_all_source_file_lists(cm_fh, scope, 'SOURCES', indent=indent + 1)

write_source_file_list(cm_fh, scope, 'DBUS_ADAPTOR_SOURCES', ['DBUS_ADAPTORS',], indent)
write_source_file_list(cm_fh, scope, 'DBUS_ADAPTOR_SOURCES', ['DBUS_ADAPTORS',], indent + 1)
dbus_adaptor_flags = scope.expand('QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS')
if dbus_adaptor_flags:
cm_fh.write('{} DBUS_ADAPTOR_FLAGS\n'.format(ind))
cm_fh.write('{} "{}"\n'.format(ind, '" "'.join(dbus_adaptor_flags)))

write_source_file_list(cm_fh, scope, 'DBUS_INTERFACE_SOURCES', ['DBUS_INTERFACES',], indent)
write_source_file_list(cm_fh, scope, 'DBUS_INTERFACE_SOURCES', ['DBUS_INTERFACES',], indent + 1)
dbus_interface_flags = scope.expand('QDBUSXML2CPP_INTERFACE_HEADER_FLAGS')
if dbus_interface_flags:
cm_fh.write('{} DBUS_INTERFACE_FLAGS\n'.format(ind))
cm_fh.write('{} "{}"\n'.format(ind, '" "'.join(dbus_interface_flags)))

defines = scope.expand('DEFINES')
defines += [d[2:] for d in scope.expand('QMAKE_CXXFLAGS') if d.startswith('-D')]
if defines:
cm_fh.write('{} DEFINES\n'.format(ind))
for d in defines:
d = d.replace('=\\\\\\"$$PWD/\\\\\\"',
'="${CMAKE_CURRENT_SOURCE_DIR}/"')
cm_fh.write('{} {}\n'.format(ind, d))
includes = scope.get_files('INCLUDEPATH')
if includes:
cm_fh.write('{} INCLUDE_DIRECTORIES\n'.format(ind))
for i in includes:
i = i.rstrip('/') or ('/')
cm_fh.write('{} {}\n'.format(ind, i))
write_defines(cm_fh, scope, 'DEFINES', indent=indent + 1)

write_include_paths(cm_fh, scope, 'INCLUDE_DIRECTORIES', indent=indent + 1)

write_library_section(cm_fh, scope,
['QMAKE_USE', 'LIBS'],
Expand All @@ -1040,11 +1079,7 @@ def write_sources_section(cm_fh: typing.IO[str], scope: Scope, *,
['QT',],
indent=indent, known_libraries=known_libraries)

compile_options = [d for d in scope.expand('QMAKE_CXXFLAGS') if not d.startswith('-D')]
if compile_options:
cm_fh.write('{} COMPILE_OPTIONS\n'.format(ind))
for co in compile_options:
cm_fh.write('{} "{}"\n'.format(ind, co))
write_compile_options(cm_fh, scope, 'COMPILE_OPTIONS', indent=indent + 1)

link_options = scope.get('QMAKE_LFLAGS')
if link_options:
Expand Down Expand Up @@ -1482,6 +1517,43 @@ def write_binary(cm_fh: typing.IO[str], scope: Scope,
known_libraries={'Qt::Core', }, extra_keys=['target.path', 'INSTALLS'])


def write_example(cm_fh: typing.IO[str], scope: Scope,
gui: bool = False, *, indent: int = 0) -> None:
binary_name = scope.TARGET
assert binary_name

#find_package(Qt5 COMPONENTS Widgets REQUIRED)
#target_link_libraries(mimetypebrowser Qt::Widgets)

cm_fh.write('cmake_minimum_required(VERSION 3.14)\n' +
'project(mimetypebrowser LANGUAGES CXX)\n\n' +
'set(CMAKE_INCLUDE_CURRENT_DIR ON)\n\n' +
'set(CMAKE_AUTOMOC ON)\n' +
'set(CMAKE_AUTORCC ON)\n' +
'set(CMAKE_AUTOUIC ON)\n\n' +
'set(INSTALL_EXAMPLEDIR "examples")\n\n')

add_executable = 'add_executable({}'.format(binary_name);
if gui:
add_executable += ' WIN32_EXECUTABLE MACOSX_BUNDLE'

write_all_source_file_lists(cm_fh, scope, add_executable, indent=0)

cm_fh.write(')\n')

write_include_paths(cm_fh, scope, 'target_include_directories({}'.format(binary_name),
indent=0, footer=')')
write_defines(cm_fh, scope, 'target_compile_definitions({}'.format(binary_name),
indent=0, footer=')')
write_compile_options(cm_fh, scope, 'target_compile_options({}'.format(binary_name),
indent=0, footer=')')

cm_fh.write('\ninstall(TARGETS mimetypebrowser\n' +
' RUNTIME_DESTINATION "${INSTALL_EXAMPLEDIR}"\n' +
' BUNDLE_DESTINATION "${INSTALL_EXAMPLESDIR}"\n' +
')\n')


def write_plugin(cm_fh, scope, *, indent: int = 0):
plugin_name = scope.TARGET
assert plugin_name
Expand All @@ -1491,52 +1563,59 @@ def write_plugin(cm_fh, scope, *, indent: int = 0):


def handle_app_or_lib(scope: Scope, cm_fh: typing.IO[str], *,
indent: int = 0) -> None:
indent: int = 0, is_example: bool=False) -> None:
assert scope.TEMPLATE in ('app', 'lib')

is_lib = scope.TEMPLATE == 'lib'
is_plugin = any('qt_plugin' == s for s in scope.get('_LOADED'))

if is_lib or 'qt_module' in scope.get('_LOADED'):
assert not is_example
write_module(cm_fh, scope, indent=indent)
elif is_plugin:
assert not is_example
write_plugin(cm_fh, scope, indent=indent)
elif 'qt_tool' in scope.get('_LOADED'):
assert not is_example
write_tool(cm_fh, scope, indent=indent)
else:
if 'testcase' in scope.get('CONFIG') \
or 'testlib' in scope.get('CONFIG'):
assert not is_example
write_test(cm_fh, scope, indent=indent)
else:
gui = 'console' not in scope.get('CONFIG')
write_binary(cm_fh, scope, gui, indent=indent)
if is_example:
write_example(cm_fh, scope, gui, indent=indent)
else:
write_binary(cm_fh, scope, gui, indent=indent)

ind = spaces(indent)
write_source_file_list(cm_fh, scope, '',
['QMAKE_DOCS',],
indent,
indent + 1,
header = '{}add_qt_docs(\n'.format(ind),
footer = '{})\n'.format(ind))


def cmakeify_scope(scope: Scope, cm_fh: typing.IO[str], *,
indent: int = 0) -> None:
indent: int = 0, is_example: bool=False) -> None:
template = scope.TEMPLATE
if template == 'subdirs':
handle_subdir(scope, cm_fh, indent=indent)
handle_subdir(scope, cm_fh, indent=indent, is_example=is_example)
elif template in ('app', 'lib'):
handle_app_or_lib(scope, cm_fh, indent=indent)
handle_app_or_lib(scope, cm_fh, indent=indent, is_example=is_example)
else:
print(' XXXX: {}: Template type {} not yet supported.'
.format(scope.file, template))


def generate_cmakelists(scope: Scope) -> None:
def generate_cmakelists(scope: Scope, *, is_example: bool=False) -> None:
with open(scope.cMakeListsFile, 'w') as cm_fh:
assert scope.file
cm_fh.write('# Generated from {}.\n\n'
.format(os.path.basename(scope.file)))
cmakeify_scope(scope, cm_fh)
cmakeify_scope(scope, cm_fh, is_example=is_example)


def do_include(scope: Scope, *, debug: bool = False) -> None:
Expand Down Expand Up @@ -1600,7 +1679,7 @@ def main() -> None:
print(file_scope.dump())
print('\n#### End of full .pro/.pri file structure.\n')

generate_cmakelists(file_scope)
generate_cmakelists(file_scope, is_example=args.is_example)
os.chdir(backup_current_dir)


Expand Down

0 comments on commit 5608bf3

Please sign in to comment.