Repository: SFTtech/openage Branch: master Commit: 865bd548d5bc Files: 1669 Total size: 7.2 MB Directory structure: gitextract_3ogk531m/ ├── .clang-format ├── .github/ │ ├── PULL_REQUEST_TEMPLATES/ │ │ ├── pull_request_template.md │ │ └── release_template.md │ └── workflows/ │ ├── macosx-ci.yml │ ├── ubuntu-24.04.yml │ ├── windows-server-2019.yml │ └── windows-server-2022.yml ├── .gitignore ├── .mailmap ├── CMakeLists.txt ├── Makefile ├── README.md ├── assets/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── logo/ │ │ └── CMakeLists.txt │ ├── qml/ │ │ ├── .gitignore │ │ ├── Actions.qml │ │ ├── ActionsGrid.qml │ │ ├── BindsHelp.qml │ │ ├── ButtonExtruded.qml │ │ ├── ButtonExtrudedStyle.qml │ │ ├── ButtonFlat.qml │ │ ├── ButtonFlatStyle.qml │ │ ├── CheckBoxFlat.qml │ │ ├── CheckBoxFlatStyle.qml │ │ ├── CreateGameWhenReady.qml │ │ ├── GeneratorControl.qml │ │ ├── GeneratorParametersConfiguration.qml │ │ ├── IngameHud.qml │ │ ├── Paper.qml │ │ ├── TextFieldFlat.qml │ │ ├── TextFieldFlatStyle.qml │ │ ├── TypePicker.qml │ │ └── main.qml │ ├── shaders/ │ │ ├── CMakeLists.txt │ │ ├── alphamask.frag.glsl │ │ ├── alphamask.vert.glsl │ │ ├── equalsEpsilon.glsl │ │ ├── final.frag.glsl │ │ ├── final.vert.glsl │ │ ├── hud_drag_select.frag.glsl │ │ ├── hud_drag_select.vert.glsl │ │ ├── identity.vert.glsl │ │ ├── maptexture.frag.glsl │ │ ├── maptexture.vert.glsl │ │ ├── skybox.frag.glsl │ │ ├── skybox.vert.glsl │ │ ├── teamcolors.frag.glsl │ │ ├── terrain.frag.glsl │ │ ├── terrain.vert.glsl │ │ ├── texturefont.frag.glsl │ │ ├── texturefont.vert.glsl │ │ ├── world2d.frag.glsl │ │ ├── world2d.vert.glsl │ │ └── world3d.vert.glsl │ ├── test/ │ │ ├── CMakeLists.txt │ │ ├── nyan/ │ │ │ ├── CMakeLists.txt │ │ │ └── pong.nyan │ │ ├── qml/ │ │ │ ├── CMakeLists.txt │ │ │ └── main.qml │ │ ├── shaders/ │ │ │ ├── CMakeLists.txt │ │ │ ├── demo_0_display.frag.glsl │ │ │ ├── demo_0_display.vert.glsl │ │ │ ├── demo_1_display.frag.glsl │ │ │ ├── demo_1_display.vert.glsl │ │ │ ├── demo_1_obj.frag.glsl │ │ │ ├── demo_1_obj.vert.glsl │ │ │ ├── demo_2_display.frag.glsl │ │ │ ├── demo_2_display.vert.glsl │ │ │ ├── demo_2_obj.frag.glsl │ │ │ ├── demo_2_obj.vert.glsl │ │ │ ├── demo_4_display.frag.glsl │ │ │ ├── demo_4_display.vert.glsl │ │ │ ├── demo_4_obj.frag.glsl │ │ │ ├── demo_4_obj.vert.glsl │ │ │ ├── demo_5_obj.frag.glsl │ │ │ ├── demo_5_obj.vert.glsl │ │ │ ├── demo_6_2d.frag.glsl │ │ │ ├── demo_6_2d.vert.glsl │ │ │ ├── demo_6_2d_frame.frag.glsl │ │ │ ├── demo_6_2d_frame.vert.glsl │ │ │ ├── demo_6_2d_frustum_frame.vert.glsl │ │ │ ├── demo_6_3d.frag.glsl │ │ │ ├── demo_6_3d.vert.glsl │ │ │ ├── demo_6_display.frag.glsl │ │ │ ├── demo_6_display.vert.glsl │ │ │ ├── demo_7_shader_command.frag.glsl │ │ │ ├── demo_7_shader_command.vert.glsl │ │ │ ├── demo_7_snippets/ │ │ │ │ ├── alpha.snippet │ │ │ │ └── color.snippet │ │ │ └── pathfinding/ │ │ │ ├── CMakeLists.txt │ │ │ ├── demo_0_cost_field.frag.glsl │ │ │ ├── demo_0_cost_field.vert.glsl │ │ │ ├── demo_0_display.frag.glsl │ │ │ ├── demo_0_display.vert.glsl │ │ │ ├── demo_0_flow_field.frag.glsl │ │ │ ├── demo_0_flow_field.vert.glsl │ │ │ ├── demo_0_grid.frag.glsl │ │ │ ├── demo_0_grid.vert.glsl │ │ │ ├── demo_0_integration_field.frag.glsl │ │ │ ├── demo_0_integration_field.vert.glsl │ │ │ ├── demo_0_obj.frag.glsl │ │ │ ├── demo_0_obj.vert.glsl │ │ │ ├── demo_0_vector.frag.glsl │ │ │ ├── demo_0_vector.vert.glsl │ │ │ ├── demo_1_display.frag.glsl │ │ │ ├── demo_1_display.vert.glsl │ │ │ ├── demo_1_grid.frag.glsl │ │ │ ├── demo_1_grid.vert.glsl │ │ │ ├── demo_1_obj.frag.glsl │ │ │ └── demo_1_obj.vert.glsl │ │ └── textures/ │ │ ├── CMakeLists.txt │ │ ├── test_animation.sprite │ │ ├── test_gaben.sprite │ │ ├── test_gaben.texture │ │ ├── test_missing.sprite │ │ ├── test_missing.texture │ │ ├── test_tank.sprite │ │ ├── test_tank.texture │ │ ├── test_tank_mirrored.sprite │ │ ├── test_terrain.terrain │ │ ├── test_terrain.texture │ │ ├── test_terrain2.terrain │ │ ├── test_terrain2.texture │ │ └── test_texture.texture │ └── textures/ │ ├── CMakeLists.txt │ └── torn_paper_edge.docx ├── buildsystem/ │ ├── CheckCompilerFeatures.cmake │ ├── CheckInSourceBuild.cmake │ ├── CheckRuntimeDependencies.cmake │ ├── DependencyFetch.cmake │ ├── DetectProjectVersion.cmake │ ├── HandleCXXOptions.cmake │ ├── HandlePythonOptions.cmake │ ├── __init__.py │ ├── check_py_file_list.py │ ├── codecompliance/ │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── authors.py │ │ ├── clangtidy.py │ │ ├── cppstyle.py │ │ ├── cython.py │ │ ├── headerguards.py │ │ ├── legal.py │ │ ├── modes.py │ │ ├── pylint.py │ │ ├── pystyle.py │ │ ├── textfiles.py │ │ └── util.py │ ├── codegen.cmake │ ├── compilepy.py │ ├── cpp.cmake │ ├── cythonize.py │ ├── doxygen.cmake │ ├── doxygen_custom.css │ ├── inplacemodules.py │ ├── modules/ │ │ ├── DownloadCache.cmake │ │ ├── FindCython.cmake │ │ ├── FindEpoxy.cmake │ │ ├── FindGCCBacktrace.cmake │ │ ├── FindGPerfTools.cmake │ │ ├── FindHarfBuzz.cmake │ │ ├── FindInotify.cmake │ │ ├── FindOgg.cmake │ │ ├── FindOpusfile.cmake │ │ ├── FindPython.cmake │ │ └── FindPython_test.cpp │ ├── options.cmake │ ├── pxdgen.py │ ├── python.cmake │ ├── scripts/ │ │ ├── EmbedFont.cmake │ │ ├── EmbedPython.cmake │ │ ├── EmbedWinDependencies.cmake │ │ └── copy_modules.py │ ├── templates/ │ │ ├── Doxyfile.in │ │ ├── ExternalFetch.cmake.in │ │ ├── ForwardVariables.cmake.in │ │ ├── openage.bat.in │ │ └── qt.conf │ └── util.cmake ├── cfg/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── converter/ │ │ └── games/ │ │ ├── aoc/ │ │ │ └── version_hashes.toml │ │ ├── aoc_demo/ │ │ │ └── version_hashes.toml │ │ ├── aok/ │ │ │ └── version_hashes.toml │ │ ├── de1/ │ │ │ └── version_hashes.toml │ │ ├── de2/ │ │ │ └── version_hashes.toml │ │ ├── game_editions.toml │ │ ├── game_expansions.toml │ │ ├── hd/ │ │ │ └── version_hashes.toml │ │ ├── hd_ak/ │ │ │ └── version_hashes.toml │ │ ├── hd_fgt/ │ │ │ └── version_hashes.toml │ │ ├── hd_raj/ │ │ │ └── version_hashes.toml │ │ ├── ror/ │ │ │ └── version_hashes.toml │ │ ├── swgb/ │ │ │ └── version_hashes.toml │ │ └── swgb_cc/ │ │ └── version_hashes.toml │ └── keybinds.oac ├── changelog.md ├── configure ├── copying.md ├── dist/ │ ├── CMakeLists.txt │ ├── README.md │ └── openage.desktop ├── doc/ │ ├── README.md │ ├── assets.md │ ├── build_instructions/ │ │ ├── arch_linux.md │ │ ├── debian.md │ │ ├── docker.md │ │ ├── fedora.md │ │ ├── freebsd.md │ │ ├── gentoo.md │ │ ├── macos.md │ │ ├── nix.md │ │ ├── opensuse.md │ │ ├── ubuntu.md │ │ └── windows_msvc.md │ ├── building.md │ ├── buildsystem.md │ ├── changelogs/ │ │ ├── engine/ │ │ │ ├── v0.0.0.md │ │ │ ├── v0.1.0.md │ │ │ ├── v0.2.0.md │ │ │ ├── v0.2.1.md │ │ │ ├── v0.2.2.md │ │ │ ├── v0.2.3.md │ │ │ ├── v0.3.0-alpha.md │ │ │ ├── v0.3.0.md │ │ │ ├── v0.4.0.md │ │ │ ├── v0.4.1.md │ │ │ ├── v0.5.0.md │ │ │ ├── v0.5.1.md │ │ │ ├── v0.5.2.md │ │ │ ├── v0.5.3.md │ │ │ └── v0.6.0.md │ │ └── nyan_api/ │ │ ├── v0.1.0.md │ │ ├── v0.2.0.md │ │ ├── v0.3.0.md │ │ ├── v0.4.0.md │ │ ├── v0.4.1.md │ │ ├── v0.5.0.md │ │ └── v0.6.0.md │ ├── code/ │ │ ├── README.md │ │ ├── architecture.md │ │ ├── converter/ │ │ │ ├── README.md │ │ │ ├── architecture_overview.md │ │ │ └── workflow.md │ │ ├── coordinate-systems.md │ │ ├── curves.md │ │ ├── event_system.md │ │ ├── exceptions.md │ │ ├── game_simulation/ │ │ │ ├── README.md │ │ │ ├── activity.md │ │ │ ├── components.md │ │ │ ├── game_entity.md │ │ │ └── systems.md │ │ ├── gui.md │ │ ├── images/ │ │ │ ├── component_classes.uxf │ │ │ ├── continuous_curve.ggb │ │ │ ├── curves_classes.uxf │ │ │ ├── discrete_curve.ggb │ │ │ ├── engine_architecture.uxf │ │ │ ├── event_classes.uxf │ │ │ ├── game_entity_classes.uxf │ │ │ ├── pathfinder_architecture.uxf │ │ │ ├── segmented_curve.ggb │ │ │ ├── simulation.uxf │ │ │ ├── system_classes.uxf │ │ │ └── time_classes.uxf │ │ ├── input/ │ │ │ └── README.md │ │ ├── logger.md │ │ ├── optimization.md │ │ ├── pathfinding/ │ │ │ ├── README.md │ │ │ └── field_types.md │ │ ├── pyinterface.md │ │ ├── renderer/ │ │ │ ├── README.md │ │ │ ├── demos.md │ │ │ ├── level1.md │ │ │ └── level2.md │ │ ├── testing.md │ │ └── time.md │ ├── contributing.md │ ├── convert/ │ │ ├── README.md │ │ ├── convert_single_file.md │ │ ├── debugger.md │ │ ├── interactive.md │ │ └── nyan.md │ ├── debug.md │ ├── development.md │ ├── ide/ │ │ ├── README.md │ │ ├── configs/ │ │ │ └── vscode/ │ │ │ └── tasks.json │ │ ├── emacs.md │ │ ├── qt_creator.md │ │ └── vscode.md │ ├── ideas/ │ │ ├── ai.md │ │ ├── editor/ │ │ │ ├── README.md │ │ │ ├── campaigns.md │ │ │ ├── economy.md │ │ │ ├── multiplayer.md │ │ │ ├── scripting.md │ │ │ ├── tech.md │ │ │ ├── terrain-ui.txt │ │ │ ├── terrain.md │ │ │ └── units.md │ │ ├── fr_technical_overview.md │ │ ├── gameplay.md │ │ ├── interface.md │ │ └── technical.md │ ├── media/ │ │ ├── aoc-slp-list.md │ │ ├── blendomatic.md │ │ ├── drs-files.md │ │ ├── evolution-openage-gource.md │ │ ├── filtermaps-dat.md │ │ ├── lzx_compression_info.md │ │ ├── openage/ │ │ │ ├── blendmask_format_spec.md │ │ │ ├── blendtable_format_spec.md │ │ │ ├── file_referencing.md │ │ │ ├── modpack_definition_file.md │ │ │ ├── modpacks.md │ │ │ ├── palette_format_spec.md │ │ │ ├── sprite_format_spec.md │ │ │ ├── terrain_format_spec.md │ │ │ └── texture_format_spec.md │ │ ├── original-metadata.md │ │ ├── patterns/ │ │ │ ├── sld.hexpat │ │ │ ├── slp.hexpat │ │ │ └── slp_v4.hexpat │ │ ├── sld-files.md │ │ ├── slp-files.md │ │ ├── smp-files.md │ │ ├── smx-files.md │ │ ├── sound.md │ │ ├── stemplet-dat.md │ │ └── terrain.md │ ├── media_convert.md │ ├── nyan/ │ │ ├── README.md │ │ ├── aoe2_nyan_tree.uxf │ │ ├── api_reference/ │ │ │ ├── reference_ability.md │ │ │ ├── reference_effect.md │ │ │ ├── reference_modifier.md │ │ │ ├── reference_resistance.md │ │ │ ├── reference_root.md │ │ │ └── reference_util.md │ │ └── openage-lib.md │ ├── project_structure.md │ ├── releasing.md │ ├── reverse_engineering/ │ │ ├── aoc_structures_00_09_26_0809 │ │ ├── civilizations.md │ │ ├── game_mechanics/ │ │ │ ├── accuracy.md │ │ │ ├── attacking_alarm.md │ │ │ ├── build_speed.md │ │ │ ├── building_placement.md │ │ │ ├── damage.md │ │ │ ├── formations.md │ │ │ ├── garrison.md │ │ │ ├── market.md │ │ │ ├── monk_conversion.md │ │ │ ├── pathfinding.md │ │ │ ├── ram_speed.md │ │ │ ├── rates.md │ │ │ ├── repair.md │ │ │ ├── research.md │ │ │ ├── selection.md │ │ │ ├── swgb/ │ │ │ │ ├── power.md │ │ │ │ ├── shields.md │ │ │ │ ├── stealth.md │ │ │ │ └── user-interface.md │ │ │ ├── switching_villager_tasks.md │ │ │ ├── town_bell.md │ │ │ ├── waypoints.md │ │ │ └── wolves.md │ │ ├── networking/ │ │ │ ├── 01-general.md │ │ │ ├── 02-header.md │ │ │ ├── 03-sync.md │ │ │ ├── 04-lobby.md │ │ │ ├── 05-chat_protocol.md │ │ │ ├── 06-chat_message_spoofing.md │ │ │ ├── 07-primary_action.md │ │ │ ├── 08-movement.md │ │ │ ├── 09-formation.md │ │ │ ├── 10-unit_creation.md │ │ │ ├── 11-buildings.md │ │ │ ├── 12-market.md │ │ │ ├── 13-other.md │ │ │ ├── 14-ai.md │ │ │ ├── 15-c-structs.md │ │ │ └── technology_ids.md │ │ ├── scoring.md │ │ └── unit_stats/ │ │ ├── unit_stats.md │ │ ├── unit_stats_afr.csv │ │ ├── unit_stats_aoc.csv │ │ ├── unit_stats_aoe.csv │ │ ├── unit_stats_aoe.txt │ │ ├── unit_stats_aok.csv │ │ └── unit_stats_fe.csv │ ├── running.md │ └── troubleshooting.md ├── etc/ │ ├── gdb_pretty/ │ │ ├── __init__.py │ │ └── printers.py │ ├── lsan.supp │ ├── openage.gdbinit │ ├── pylintrc │ └── valgrind-python.supp ├── flake.nix ├── kevinfile ├── legal/ │ ├── BSD-3-clause │ ├── GPLv3 │ └── LGPLv2.0 ├── libopenage/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── assets/ │ │ ├── CMakeLists.txt │ │ ├── mod_manager.cpp │ │ ├── mod_manager.h │ │ ├── modpack.cpp │ │ └── modpack.h │ ├── audio/ │ │ ├── CMakeLists.txt │ │ ├── audio_manager.cpp │ │ ├── audio_manager.h │ │ ├── category.cpp │ │ ├── category.h │ │ ├── dynamic_loader.cpp │ │ ├── dynamic_loader.h │ │ ├── dynamic_resource.cpp │ │ ├── dynamic_resource.h │ │ ├── error.cpp │ │ ├── error.h │ │ ├── format.cpp │ │ ├── format.h │ │ ├── hash_functions.h │ │ ├── in_memory_loader.cpp │ │ ├── in_memory_loader.h │ │ ├── in_memory_resource.cpp │ │ ├── in_memory_resource.h │ │ ├── loader_policy.cpp │ │ ├── loader_policy.h │ │ ├── opus_dynamic_loader.cpp │ │ ├── opus_dynamic_loader.h │ │ ├── opus_in_memory_loader.cpp │ │ ├── opus_in_memory_loader.h │ │ ├── opus_loading.cpp │ │ ├── opus_loading.h │ │ ├── resource.cpp │ │ ├── resource.h │ │ ├── resource_def.cpp │ │ ├── resource_def.h │ │ ├── sound.cpp │ │ ├── sound.h │ │ └── types.h │ ├── config.cpp.in │ ├── config.h.in │ ├── console/ │ │ ├── CMakeLists.txt │ │ ├── buf.cpp │ │ ├── buf.h │ │ ├── console.cpp │ │ ├── console.h │ │ ├── draw.cpp │ │ ├── draw.h │ │ └── tests.cpp │ ├── coord/ │ │ ├── CMakeLists.txt │ │ ├── chunk.cpp │ │ ├── chunk.h │ │ ├── coord.cpp.template │ │ ├── coord.h.template │ │ ├── coord_test.cpp │ │ ├── declarations.cpp │ │ ├── declarations.h │ │ ├── phys.cpp │ │ ├── phys.h │ │ ├── pixel.cpp │ │ ├── pixel.h │ │ ├── scene.cpp │ │ ├── scene.h │ │ ├── term.cpp │ │ ├── term.h │ │ ├── tile.cpp │ │ └── tile.h │ ├── curve/ │ │ ├── CMakeLists.txt │ │ ├── base_curve.cpp │ │ ├── base_curve.h │ │ ├── container/ │ │ │ ├── CMakeLists.txt │ │ │ ├── array.cpp │ │ │ ├── array.h │ │ │ ├── element_wrapper.cpp │ │ │ ├── element_wrapper.h │ │ │ ├── iterator.cpp │ │ │ ├── iterator.h │ │ │ ├── map.cpp │ │ │ ├── map.h │ │ │ ├── map_filter_iterator.cpp │ │ │ ├── map_filter_iterator.h │ │ │ ├── queue.cpp │ │ │ ├── queue.h │ │ │ ├── queue_filter_iterator.cpp │ │ │ └── queue_filter_iterator.h │ │ ├── continuous.cpp │ │ ├── continuous.h │ │ ├── discrete.cpp │ │ ├── discrete.h │ │ ├── discrete_mod.cpp │ │ ├── discrete_mod.h │ │ ├── interpolated.cpp │ │ ├── interpolated.h │ │ ├── keyframe.cpp │ │ ├── keyframe.h │ │ ├── keyframe_container.cpp │ │ ├── keyframe_container.h │ │ ├── segmented.cpp │ │ ├── segmented.h │ │ └── tests/ │ │ ├── CMakeLists.txt │ │ ├── container.cpp │ │ └── curve_types.cpp │ ├── cvar/ │ │ ├── CMakeLists.txt │ │ ├── cvar.cpp │ │ └── cvar.h │ ├── datastructure/ │ │ ├── CMakeLists.txt │ │ ├── concurrent_queue.h │ │ ├── constexpr_map.h │ │ ├── pairing_heap.h │ │ ├── tests.cpp │ │ └── tests.h │ ├── engine/ │ │ ├── CMakeLists.txt │ │ ├── engine.cpp │ │ └── engine.h │ ├── error/ │ │ ├── CMakeLists.txt │ │ ├── backtrace.cpp │ │ ├── backtrace.h │ │ ├── demo.cpp │ │ ├── error.cpp │ │ ├── error.h │ │ ├── handlers.cpp │ │ ├── handlers.h │ │ ├── stackanalyzer.cpp │ │ └── stackanalyzer.h │ ├── event/ │ │ ├── CMakeLists.txt │ │ ├── demo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── aicontroller.cpp │ │ │ ├── aicontroller.h │ │ │ ├── gamestate.cpp │ │ │ ├── gamestate.h │ │ │ ├── gui.cpp │ │ │ ├── gui.h │ │ │ ├── main.cpp │ │ │ ├── main.h │ │ │ ├── physics.cpp │ │ │ └── physics.h │ │ ├── event.cpp │ │ ├── event.h │ │ ├── event_loop.cpp │ │ ├── event_loop.h │ │ ├── evententity.cpp │ │ ├── evententity.h │ │ ├── eventhandler.cpp │ │ ├── eventhandler.h │ │ ├── eventqueue.cpp │ │ ├── eventqueue.h │ │ ├── eventstore.cpp │ │ ├── eventstore.h │ │ ├── state.cpp │ │ ├── state.h │ │ └── tests.cpp │ ├── gamestate/ │ │ ├── CMakeLists.txt │ │ ├── activity/ │ │ │ ├── CMakeLists.txt │ │ │ ├── activity.cpp │ │ │ ├── activity.h │ │ │ ├── condition/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── command_in_queue.cpp │ │ │ │ ├── command_in_queue.h │ │ │ │ ├── next_command.cpp │ │ │ │ └── next_command.h │ │ │ ├── end_node.cpp │ │ │ ├── end_node.h │ │ │ ├── event/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── command_in_queue.cpp │ │ │ │ ├── command_in_queue.h │ │ │ │ ├── wait.cpp │ │ │ │ └── wait.h │ │ │ ├── node.cpp │ │ │ ├── node.h │ │ │ ├── start_node.cpp │ │ │ ├── start_node.h │ │ │ ├── task_node.cpp │ │ │ ├── task_node.h │ │ │ ├── task_system_node.cpp │ │ │ ├── task_system_node.h │ │ │ ├── tests.cpp │ │ │ ├── types.cpp │ │ │ ├── types.h │ │ │ ├── xor_event_gate.cpp │ │ │ ├── xor_event_gate.h │ │ │ ├── xor_gate.cpp │ │ │ └── xor_gate.h │ │ ├── api/ │ │ │ ├── CMakeLists.txt │ │ │ ├── ability.cpp │ │ │ ├── ability.h │ │ │ ├── activity.cpp │ │ │ ├── activity.h │ │ │ ├── animation.cpp │ │ │ ├── animation.h │ │ │ ├── definitions.cpp │ │ │ ├── definitions.h │ │ │ ├── patch.cpp │ │ │ ├── patch.h │ │ │ ├── player_setup.cpp │ │ │ ├── player_setup.h │ │ │ ├── property.cpp │ │ │ ├── property.h │ │ │ ├── sound.cpp │ │ │ ├── sound.h │ │ │ ├── terrain.cpp │ │ │ ├── terrain.h │ │ │ ├── types.cpp │ │ │ ├── types.h │ │ │ ├── util.cpp │ │ │ └── util.h │ │ ├── component/ │ │ │ ├── CMakeLists.txt │ │ │ ├── api/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── idle.cpp │ │ │ │ ├── idle.h │ │ │ │ ├── live.cpp │ │ │ │ ├── live.h │ │ │ │ ├── move.cpp │ │ │ │ ├── move.h │ │ │ │ ├── selectable.cpp │ │ │ │ ├── selectable.h │ │ │ │ ├── turn.cpp │ │ │ │ └── turn.h │ │ │ ├── api_component.cpp │ │ │ ├── api_component.h │ │ │ ├── base_component.cpp │ │ │ ├── base_component.h │ │ │ ├── internal/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── activity.cpp │ │ │ │ ├── activity.h │ │ │ │ ├── command_queue.cpp │ │ │ │ ├── command_queue.h │ │ │ │ ├── commands/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── base_command.cpp │ │ │ │ │ ├── base_command.h │ │ │ │ │ ├── custom.cpp │ │ │ │ │ ├── custom.h │ │ │ │ │ ├── idle.cpp │ │ │ │ │ ├── idle.h │ │ │ │ │ ├── move.cpp │ │ │ │ │ ├── move.h │ │ │ │ │ ├── types.cpp │ │ │ │ │ └── types.h │ │ │ │ ├── ownership.cpp │ │ │ │ ├── ownership.h │ │ │ │ ├── position.cpp │ │ │ │ └── position.h │ │ │ ├── internal_component.cpp │ │ │ ├── internal_component.h │ │ │ ├── types.cpp │ │ │ └── types.h │ │ ├── definitions.cpp │ │ ├── definitions.h │ │ ├── demo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── demo_0.cpp │ │ │ ├── demo_0.h │ │ │ ├── tests.cpp │ │ │ └── tests.h │ │ ├── entity_factory.cpp │ │ ├── entity_factory.h │ │ ├── event/ │ │ │ ├── CMakeLists.txt │ │ │ ├── drag_select.cpp │ │ │ ├── drag_select.h │ │ │ ├── process_command.cpp │ │ │ ├── process_command.h │ │ │ ├── send_command.cpp │ │ │ ├── send_command.h │ │ │ ├── spawn_entity.cpp │ │ │ ├── spawn_entity.h │ │ │ ├── wait.cpp │ │ │ └── wait.h │ │ ├── game.cpp │ │ ├── game.h │ │ ├── game_entity.cpp │ │ ├── game_entity.h │ │ ├── game_state.cpp │ │ ├── game_state.h │ │ ├── manager.cpp │ │ ├── manager.h │ │ ├── map.cpp │ │ ├── map.h │ │ ├── player.cpp │ │ ├── player.h │ │ ├── simulation.cpp │ │ ├── simulation.h │ │ ├── system/ │ │ │ ├── CMakeLists.txt │ │ │ ├── activity.cpp │ │ │ ├── activity.h │ │ │ ├── idle.cpp │ │ │ ├── idle.h │ │ │ ├── move.cpp │ │ │ ├── move.h │ │ │ ├── types.cpp │ │ │ └── types.h │ │ ├── terrain.cpp │ │ ├── terrain.h │ │ ├── terrain_chunk.cpp │ │ ├── terrain_chunk.h │ │ ├── terrain_factory.cpp │ │ ├── terrain_factory.h │ │ ├── terrain_tile.cpp │ │ ├── terrain_tile.h │ │ ├── types.cpp │ │ ├── types.h │ │ ├── universe.cpp │ │ ├── universe.h │ │ ├── world.cpp │ │ └── world.h │ ├── input/ │ │ ├── CMakeLists.txt │ │ ├── action.cpp │ │ ├── action.h │ │ ├── controller/ │ │ │ ├── CMakeLists.txt │ │ │ ├── camera/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── binding.cpp │ │ │ │ ├── binding.h │ │ │ │ ├── binding_context.cpp │ │ │ │ ├── binding_context.h │ │ │ │ ├── controller.cpp │ │ │ │ └── controller.h │ │ │ ├── game/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── binding.cpp │ │ │ │ ├── binding.h │ │ │ │ ├── binding_context.cpp │ │ │ │ ├── binding_context.h │ │ │ │ ├── controller.cpp │ │ │ │ └── controller.h │ │ │ └── hud/ │ │ │ ├── CMakeLists.txt │ │ │ ├── binding.cpp │ │ │ ├── binding.h │ │ │ ├── binding_context.cpp │ │ │ ├── binding_context.h │ │ │ ├── controller.cpp │ │ │ └── controller.h │ │ ├── event.cpp │ │ ├── event.h │ │ ├── input_context.cpp │ │ ├── input_context.h │ │ ├── input_manager.cpp │ │ ├── input_manager.h │ │ ├── tests.cpp │ │ ├── text_to_event.cpp │ │ └── text_to_event.h │ ├── job/ │ │ ├── CMakeLists.txt │ │ ├── abortable_job_state.h │ │ ├── job.h │ │ ├── job_aborted_exception.h │ │ ├── job_group.cpp │ │ ├── job_group.h │ │ ├── job_manager.cpp │ │ ├── job_manager.h │ │ ├── job_state.h │ │ ├── job_state_base.h │ │ ├── tests.cpp │ │ ├── typed_job_state_base.h │ │ ├── types.h │ │ ├── worker.cpp │ │ └── worker.h │ ├── log/ │ │ ├── CMakeLists.txt │ │ ├── file_logsink.cpp │ │ ├── file_logsink.h │ │ ├── level.cpp │ │ ├── level.h │ │ ├── log.cpp │ │ ├── log.h │ │ ├── logsink.cpp │ │ ├── logsink.h │ │ ├── logsource.cpp │ │ ├── logsource.h │ │ ├── message.cpp │ │ ├── message.h │ │ ├── named_logsource.cpp │ │ ├── named_logsource.h │ │ ├── stdout_logsink.cpp │ │ ├── stdout_logsink.h │ │ └── test.cpp │ ├── main/ │ │ ├── CMakeLists.txt │ │ ├── demo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── interactive/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── interactive.cpp │ │ │ │ └── interactive.h │ │ │ ├── pong/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── aicontroller.cpp │ │ │ │ ├── aicontroller.h │ │ │ │ ├── gamestate.cpp │ │ │ │ ├── gamestate.h │ │ │ │ ├── gui.cpp │ │ │ │ ├── gui.h │ │ │ │ ├── physics.cpp │ │ │ │ ├── physics.h │ │ │ │ ├── pong.cpp │ │ │ │ └── pong.h │ │ │ └── presenter/ │ │ │ ├── CMakeLists.txt │ │ │ ├── presenter.cpp │ │ │ └── presenter.h │ │ ├── tests.cpp │ │ └── tests.h │ ├── main.cpp │ ├── main.h │ ├── options.cpp │ ├── options.h │ ├── pathfinding/ │ │ ├── CMakeLists.txt │ │ ├── cost_field.cpp │ │ ├── cost_field.h │ │ ├── definitions.cpp │ │ ├── definitions.h │ │ ├── demo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── demo_0.cpp │ │ │ ├── demo_0.h │ │ │ ├── demo_1.cpp │ │ │ ├── demo_1.h │ │ │ ├── tests.cpp │ │ │ └── tests.h │ │ ├── field_cache.cpp │ │ ├── field_cache.h │ │ ├── flow_field.cpp │ │ ├── flow_field.h │ │ ├── grid.cpp │ │ ├── grid.h │ │ ├── integration_field.cpp │ │ ├── integration_field.h │ │ ├── integrator.cpp │ │ ├── integrator.h │ │ ├── legacy/ │ │ │ ├── CMakeLists.txt │ │ │ ├── a_star.cpp │ │ │ ├── a_star.h │ │ │ ├── heuristics.cpp │ │ │ ├── heuristics.h │ │ │ ├── path.cpp │ │ │ ├── path.h │ │ │ ├── path_utils.h │ │ │ └── tests.cpp │ │ ├── path.cpp │ │ ├── path.h │ │ ├── pathfinder.cpp │ │ ├── pathfinder.h │ │ ├── portal.cpp │ │ ├── portal.h │ │ ├── sector.cpp │ │ ├── sector.h │ │ ├── tests.cpp │ │ ├── types.cpp │ │ └── types.h │ ├── presenter/ │ │ ├── CMakeLists.txt │ │ ├── presenter.cpp │ │ └── presenter.h │ ├── pyinterface/ │ │ ├── CMakeLists.txt │ │ ├── defs.cpp │ │ ├── defs.h │ │ ├── exctranslate.cpp │ │ ├── exctranslate.h │ │ ├── exctranslate_tests.cpp │ │ ├── exctranslate_tests.h │ │ ├── functional.cpp │ │ ├── functional.h │ │ ├── hacks.cpp │ │ ├── hacks.h │ │ ├── pyexception.cpp │ │ ├── pyexception.h │ │ ├── pyobject.cpp │ │ ├── pyobject.h │ │ ├── pyobject_tests.cpp │ │ ├── pyobject_tests.h │ │ ├── setup.cpp │ │ └── setup.h │ ├── renderer/ │ │ ├── CMakeLists.txt │ │ ├── camera/ │ │ │ ├── CMakeLists.txt │ │ │ ├── boundaries.cpp │ │ │ ├── boundaries.h │ │ │ ├── camera.cpp │ │ │ ├── camera.h │ │ │ ├── definitions.cpp │ │ │ ├── definitions.h │ │ │ ├── frustum_2d.cpp │ │ │ ├── frustum_2d.h │ │ │ ├── frustum_3d.cpp │ │ │ └── frustum_3d.h │ │ ├── color.cpp │ │ ├── color.h │ │ ├── definitions.cpp │ │ ├── definitions.h │ │ ├── demo/ │ │ │ ├── CMakeLists.txt │ │ │ ├── demo_0.cpp │ │ │ ├── demo_0.h │ │ │ ├── demo_1.cpp │ │ │ ├── demo_1.h │ │ │ ├── demo_2.cpp │ │ │ ├── demo_2.h │ │ │ ├── demo_3.cpp │ │ │ ├── demo_3.h │ │ │ ├── demo_4.cpp │ │ │ ├── demo_4.h │ │ │ ├── demo_5.cpp │ │ │ ├── demo_5.h │ │ │ ├── demo_6.cpp │ │ │ ├── demo_6.h │ │ │ ├── demo_7.cpp │ │ │ ├── demo_7.h │ │ │ ├── stresstest_0.cpp │ │ │ ├── stresstest_0.h │ │ │ ├── stresstest_1.cpp │ │ │ ├── stresstest_1.h │ │ │ ├── tests.cpp │ │ │ ├── tests.h │ │ │ ├── util.cpp │ │ │ └── util.h │ │ ├── font/ │ │ │ ├── CMakeLists.txt │ │ │ ├── font.cpp │ │ │ ├── font.h │ │ │ ├── font_manager.cpp │ │ │ ├── font_manager.h │ │ │ ├── glyph_atlas.cpp │ │ │ ├── glyph_atlas.h │ │ │ └── tests.cpp │ │ ├── geometry.cpp │ │ ├── geometry.h │ │ ├── gui/ │ │ │ ├── CMakeLists.txt │ │ │ ├── gui.cpp │ │ │ ├── gui.h │ │ │ ├── guisys/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── link/ │ │ │ │ │ ├── gui_item.cpp │ │ │ │ │ ├── gui_item.h │ │ │ │ │ ├── gui_item_link.h │ │ │ │ │ ├── gui_item_list_model.h │ │ │ │ │ ├── gui_list_model.cpp │ │ │ │ │ ├── gui_list_model.h │ │ │ │ │ ├── gui_property_map_impl.cpp │ │ │ │ │ ├── gui_property_map_impl.h │ │ │ │ │ ├── gui_singleton_item.cpp │ │ │ │ │ ├── gui_singleton_item.h │ │ │ │ │ └── qtgui_checked_static_cast.h │ │ │ │ ├── private/ │ │ │ │ │ ├── gui_application_impl.cpp │ │ │ │ │ ├── gui_application_impl.h │ │ │ │ │ ├── gui_ctx_setup.cpp │ │ │ │ │ ├── gui_ctx_setup.h │ │ │ │ │ ├── gui_engine_impl.cpp │ │ │ │ │ ├── gui_engine_impl.h │ │ │ │ │ ├── gui_input_impl.cpp │ │ │ │ │ ├── gui_input_impl.h │ │ │ │ │ ├── gui_renderer_impl.cpp │ │ │ │ │ ├── gui_renderer_impl.h │ │ │ │ │ ├── gui_rendering_setup_routines.cpp │ │ │ │ │ ├── gui_rendering_setup_routines.h │ │ │ │ │ ├── gui_subtree_impl.cpp │ │ │ │ │ ├── gui_subtree_impl.h │ │ │ │ │ ├── livereload/ │ │ │ │ │ │ ├── deferred_initial_constant_property_values.cpp │ │ │ │ │ │ ├── deferred_initial_constant_property_values.h │ │ │ │ │ │ ├── gui_live_reloader.cpp │ │ │ │ │ │ ├── gui_live_reloader.h │ │ │ │ │ │ ├── recursive_directory_watcher.cpp │ │ │ │ │ │ ├── recursive_directory_watcher.h │ │ │ │ │ │ ├── recursive_directory_watcher_worker.cpp │ │ │ │ │ │ └── recursive_directory_watcher_worker.h │ │ │ │ │ ├── opengl_debug_logger.cpp │ │ │ │ │ └── opengl_debug_logger.h │ │ │ │ └── public/ │ │ │ │ ├── gui_application.cpp │ │ │ │ ├── gui_application.h │ │ │ │ ├── gui_engine.cpp │ │ │ │ ├── gui_engine.h │ │ │ │ ├── gui_input.cpp │ │ │ │ ├── gui_input.h │ │ │ │ ├── gui_renderer.cpp │ │ │ │ ├── gui_renderer.h │ │ │ │ ├── gui_subtree.cpp │ │ │ │ └── gui_subtree.h │ │ │ └── integration/ │ │ │ ├── CMakeLists.txt │ │ │ ├── private/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── gui_filled_texture_handles.cpp │ │ │ │ ├── gui_filled_texture_handles.h │ │ │ │ ├── gui_log.cpp │ │ │ │ ├── gui_log.h │ │ │ │ ├── gui_make_standalone_subtexture.cpp │ │ │ │ ├── gui_make_standalone_subtexture.h │ │ │ │ ├── gui_standalone_subtexture.cpp │ │ │ │ ├── gui_standalone_subtexture.h │ │ │ │ ├── gui_texture.cpp │ │ │ │ ├── gui_texture.h │ │ │ │ ├── gui_texture_handle.cpp │ │ │ │ └── gui_texture_handle.h │ │ │ └── public/ │ │ │ ├── CMakeLists.txt │ │ │ ├── gui_application_with_logger.cpp │ │ │ └── gui_application_with_logger.h │ │ ├── opengl/ │ │ │ ├── CMakeLists.txt │ │ │ ├── buffer.cpp │ │ │ ├── buffer.h │ │ │ ├── context.cpp │ │ │ ├── context.h │ │ │ ├── debug.cpp │ │ │ ├── debug.h │ │ │ ├── error.cpp │ │ │ ├── error.h │ │ │ ├── framebuffer.cpp │ │ │ ├── framebuffer.h │ │ │ ├── geometry.cpp │ │ │ ├── geometry.h │ │ │ ├── lookup.h │ │ │ ├── render_pass.cpp │ │ │ ├── render_pass.h │ │ │ ├── render_target.cpp │ │ │ ├── render_target.h │ │ │ ├── renderer.cpp │ │ │ ├── renderer.h │ │ │ ├── shader.cpp │ │ │ ├── shader.h │ │ │ ├── shader_data.cpp │ │ │ ├── shader_data.h │ │ │ ├── shader_program.cpp │ │ │ ├── shader_program.h │ │ │ ├── simple_object.cpp │ │ │ ├── simple_object.h │ │ │ ├── texture.cpp │ │ │ ├── texture.h │ │ │ ├── texture_array.cpp │ │ │ ├── texture_array.h │ │ │ ├── uniform_buffer.cpp │ │ │ ├── uniform_buffer.h │ │ │ ├── uniform_input.cpp │ │ │ ├── uniform_input.h │ │ │ ├── util.cpp │ │ │ ├── util.h │ │ │ ├── vertex_array.cpp │ │ │ ├── vertex_array.h │ │ │ ├── window.cpp │ │ │ └── window.h │ │ ├── render_factory.cpp │ │ ├── render_factory.h │ │ ├── render_pass.cpp │ │ ├── render_pass.h │ │ ├── render_target.cpp │ │ ├── render_target.h │ │ ├── renderable.cpp │ │ ├── renderable.h │ │ ├── renderer.cpp │ │ ├── renderer.h │ │ ├── resources/ │ │ │ ├── CMakeLists.txt │ │ │ ├── animation/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── angle_info.cpp │ │ │ │ ├── angle_info.h │ │ │ │ ├── animation_info.cpp │ │ │ │ ├── animation_info.h │ │ │ │ ├── frame_info.cpp │ │ │ │ ├── frame_info.h │ │ │ │ ├── layer_info.cpp │ │ │ │ └── layer_info.h │ │ │ ├── assets/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── asset_manager.cpp │ │ │ │ ├── asset_manager.h │ │ │ │ ├── cache.cpp │ │ │ │ ├── cache.h │ │ │ │ ├── texture_manager.cpp │ │ │ │ └── texture_manager.h │ │ │ ├── buffer_info.cpp │ │ │ ├── buffer_info.h │ │ │ ├── frame_timing.cpp │ │ │ ├── frame_timing.h │ │ │ ├── mesh_data.cpp │ │ │ ├── mesh_data.h │ │ │ ├── palette_info.cpp │ │ │ ├── palette_info.h │ │ │ ├── parser/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── common.cpp │ │ │ │ ├── common.h │ │ │ │ ├── parse_blendmask.cpp │ │ │ │ ├── parse_blendmask.h │ │ │ │ ├── parse_blendtable.cpp │ │ │ │ ├── parse_blendtable.h │ │ │ │ ├── parse_palette.cpp │ │ │ │ ├── parse_palette.h │ │ │ │ ├── parse_sprite.cpp │ │ │ │ ├── parse_sprite.h │ │ │ │ ├── parse_terrain.cpp │ │ │ │ ├── parse_terrain.h │ │ │ │ ├── parse_texture.cpp │ │ │ │ └── parse_texture.h │ │ │ ├── shader_source.cpp │ │ │ ├── shader_source.h │ │ │ ├── shader_template.cpp │ │ │ ├── shader_template.h │ │ │ ├── terrain/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── blendpattern_info.cpp │ │ │ │ ├── blendpattern_info.h │ │ │ │ ├── blendtable_info.cpp │ │ │ │ ├── blendtable_info.h │ │ │ │ ├── frame_info.cpp │ │ │ │ ├── frame_info.h │ │ │ │ ├── layer_info.cpp │ │ │ │ ├── layer_info.h │ │ │ │ ├── terrain_info.cpp │ │ │ │ └── terrain_info.h │ │ │ ├── texture_data.cpp │ │ │ ├── texture_data.h │ │ │ ├── texture_info.cpp │ │ │ ├── texture_info.h │ │ │ ├── texture_subinfo.cpp │ │ │ └── texture_subinfo.h │ │ ├── shader_program.cpp │ │ ├── shader_program.h │ │ ├── stages/ │ │ │ ├── CMakeLists.txt │ │ │ ├── camera/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── manager.cpp │ │ │ │ └── manager.h │ │ │ ├── hud/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── object.cpp │ │ │ │ ├── object.h │ │ │ │ ├── render_entity.cpp │ │ │ │ ├── render_entity.h │ │ │ │ ├── render_stage.cpp │ │ │ │ └── render_stage.h │ │ │ ├── render_entity.cpp │ │ │ ├── render_entity.h │ │ │ ├── screen/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── render_stage.cpp │ │ │ │ ├── render_stage.h │ │ │ │ ├── screenshot.cpp │ │ │ │ └── screenshot.h │ │ │ ├── skybox/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── render_stage.cpp │ │ │ │ └── render_stage.h │ │ │ ├── terrain/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── chunk.cpp │ │ │ │ ├── chunk.h │ │ │ │ ├── mesh.cpp │ │ │ │ ├── mesh.h │ │ │ │ ├── model.cpp │ │ │ │ ├── model.h │ │ │ │ ├── render_entity.cpp │ │ │ │ ├── render_entity.h │ │ │ │ ├── render_stage.cpp │ │ │ │ └── render_stage.h │ │ │ └── world/ │ │ │ ├── CMakeLists.txt │ │ │ ├── object.cpp │ │ │ ├── object.h │ │ │ ├── render_entity.cpp │ │ │ ├── render_entity.h │ │ │ ├── render_stage.cpp │ │ │ └── render_stage.h │ │ ├── texture.cpp │ │ ├── texture.h │ │ ├── texture_array.cpp │ │ ├── texture_array.h │ │ ├── types.cpp │ │ ├── types.h │ │ ├── uniform_buffer.cpp │ │ ├── uniform_buffer.h │ │ ├── uniform_input.cpp │ │ ├── uniform_input.h │ │ ├── util.cpp │ │ ├── util.h │ │ ├── vulkan/ │ │ │ ├── CMakeLists.txt │ │ │ ├── README.md │ │ │ ├── graphics_device.cpp │ │ │ ├── graphics_device.h │ │ │ ├── loader.cpp │ │ │ ├── loader.h │ │ │ ├── render_target.h │ │ │ ├── renderer.cpp │ │ │ ├── renderer.h │ │ │ ├── shader_program.h │ │ │ ├── util.h │ │ │ ├── windowvk.cpp │ │ │ └── windowvk.h │ │ ├── window.cpp │ │ ├── window.h │ │ ├── window_event_handler.cpp │ │ └── window_event_handler.h │ ├── rng/ │ │ ├── CMakeLists.txt │ │ ├── global_rng.cpp │ │ ├── global_rng.h │ │ ├── rng.cpp │ │ ├── rng.h │ │ └── rng_tests.cpp │ ├── testing/ │ │ ├── CMakeLists.txt │ │ ├── benchmark_test.cpp │ │ ├── testing.cpp │ │ ├── testing.h │ │ ├── testlist.cpp.template │ │ └── testlist.h │ ├── time/ │ │ ├── CMakeLists.txt │ │ ├── clock.cpp │ │ ├── clock.h │ │ ├── time.cpp │ │ ├── time.h │ │ ├── time_loop.cpp │ │ └── time_loop.h │ ├── util/ │ │ ├── CMakeLists.txt │ │ ├── algorithm.h │ │ ├── color.cpp │ │ ├── color.h │ │ ├── compiler.cpp │ │ ├── compiler.h │ │ ├── compress/ │ │ │ ├── CMakeLists.txt │ │ │ ├── bitstream.h │ │ │ ├── lzxd.cpp │ │ │ └── lzxd.h │ │ ├── consteval.h │ │ ├── constinit_vector.cpp │ │ ├── constinit_vector.h │ │ ├── enum.cpp │ │ ├── enum.h │ │ ├── enum_test.cpp │ │ ├── enum_test.h │ │ ├── externalprofiler.cpp │ │ ├── externalprofiler.h │ │ ├── externalsstream.cpp │ │ ├── externalsstream.h │ │ ├── fds.cpp │ │ ├── fds.h │ │ ├── file.cpp │ │ ├── file.h │ │ ├── filelike/ │ │ │ ├── CMakeLists.txt │ │ │ ├── filelike.cpp │ │ │ ├── filelike.h │ │ │ ├── native.cpp │ │ │ ├── native.h │ │ │ ├── python.cpp │ │ │ └── python.h │ │ ├── fixed_point.cpp │ │ ├── fixed_point.h │ │ ├── fixed_point_test.cpp │ │ ├── fps.cpp │ │ ├── fps.h │ │ ├── fslike/ │ │ │ ├── CMakeLists.txt │ │ │ ├── directory.cpp │ │ │ ├── directory.h │ │ │ ├── fslike.cpp │ │ │ ├── fslike.h │ │ │ ├── native.cpp │ │ │ ├── native.h │ │ │ ├── python.cpp │ │ │ └── python.h │ │ ├── hash.cpp │ │ ├── hash.h │ │ ├── hash_test.cpp │ │ ├── init.cpp │ │ ├── init.h │ │ ├── language.cpp │ │ ├── language.h │ │ ├── macro/ │ │ │ ├── concat.h │ │ │ └── loop.h │ │ ├── math.h │ │ ├── math_constants.h │ │ ├── matrix.cpp │ │ ├── matrix.h │ │ ├── matrix_test.cpp │ │ ├── misc.cpp │ │ ├── misc.h │ │ ├── misc_test.cpp │ │ ├── os.cpp │ │ ├── os.h │ │ ├── path.cpp │ │ ├── path.h │ │ ├── pty.h │ │ ├── quaternion.cpp │ │ ├── quaternion.h │ │ ├── quaternion_test.cpp │ │ ├── repr.cpp │ │ ├── repr.h │ │ ├── signal.h │ │ ├── stringformatter.cpp │ │ ├── stringformatter.h │ │ ├── strings.cpp │ │ ├── strings.h │ │ ├── subprocess.cpp │ │ ├── subprocess.h │ │ ├── thread_id.cpp │ │ ├── thread_id.h │ │ ├── timer.cpp │ │ ├── timer.h │ │ ├── timing.cpp │ │ ├── timing.h │ │ ├── unicode.cpp │ │ ├── unicode.h │ │ ├── variable.h │ │ ├── vector.cpp │ │ ├── vector.h │ │ └── vector_test.cpp │ ├── version.cpp.in │ ├── version.h.in │ └── versions/ │ ├── CMakeLists.txt │ ├── compiletime.cpp.in │ ├── compiletime.h.in │ ├── versions.cpp │ └── versions.h ├── nix/ │ ├── nyan.nix │ └── openage.nix ├── openage/ │ ├── .gitignore │ ├── CMakeLists.txt │ ├── __init__.py │ ├── __main__.py │ ├── assets.py │ ├── cabextract/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── cab.py │ │ ├── cabchecksum.pyx │ │ ├── gen_test_arc.sh │ │ ├── lzxd.pyx │ │ ├── lzxdstream.py │ │ └── test.py │ ├── codegen/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── codegen.py │ │ ├── coord.py │ │ ├── cpp_testlist.py │ │ ├── listing.py │ │ └── main.py │ ├── config.py.in │ ├── convert/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── entity_object/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── conversion/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── aoc/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── genie_civ.py │ │ │ │ │ ├── genie_connection.py │ │ │ │ │ ├── genie_effect.py │ │ │ │ │ ├── genie_graphic.py │ │ │ │ │ ├── genie_object_container.py │ │ │ │ │ ├── genie_sound.py │ │ │ │ │ ├── genie_tech.py │ │ │ │ │ ├── genie_terrain.py │ │ │ │ │ └── genie_unit.py │ │ │ │ ├── combined_sound.py │ │ │ │ ├── combined_sprite.py │ │ │ │ ├── combined_terrain.py │ │ │ │ ├── converter_object.py │ │ │ │ ├── modpack.py │ │ │ │ ├── ror/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── genie_sound.py │ │ │ │ │ ├── genie_tech.py │ │ │ │ │ └── genie_unit.py │ │ │ │ ├── stringresource.py │ │ │ │ └── swgbcc/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── genie_tech.py │ │ │ │ └── genie_unit.py │ │ │ └── export/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── data_definition.py │ │ │ ├── formats/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── blmask_metadata.py │ │ │ │ ├── bltable_metadata.py │ │ │ │ ├── media_cache.py │ │ │ │ ├── modpack_info.py │ │ │ │ ├── modpack_manifest.py │ │ │ │ ├── nyan_file.py │ │ │ │ ├── palette_metadata.py │ │ │ │ ├── sprite_metadata.py │ │ │ │ ├── terrain_metadata.py │ │ │ │ └── texture_metadata.py │ │ │ ├── media_export_request.py │ │ │ ├── metadata_export.py │ │ │ └── texture.py │ │ ├── main.py │ │ ├── processor/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── conversion/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── aoc/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── ability_subprocessor.py │ │ │ │ │ ├── auxiliary_subprocessor.py │ │ │ │ │ ├── civ_subprocessor.py │ │ │ │ │ ├── effect_subprocessor.py │ │ │ │ │ ├── media_subprocessor.py │ │ │ │ │ ├── modifier_subprocessor.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ ├── nyan_subprocessor.py │ │ │ │ │ ├── pregen_processor.py │ │ │ │ │ ├── processor.py │ │ │ │ │ ├── tech_subprocessor.py │ │ │ │ │ ├── upgrade_ability_subprocessor.py │ │ │ │ │ ├── upgrade_attribute_subprocessor.py │ │ │ │ │ ├── upgrade_effect_subprocessor.py │ │ │ │ │ └── upgrade_resource_subprocessor.py │ │ │ │ ├── aoc_demo/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ └── processor.py │ │ │ │ ├── de1/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── media_subprocessor.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ └── processor.py │ │ │ │ ├── de2/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── ability_subprocessor.py │ │ │ │ │ ├── civ_subprocessor.py │ │ │ │ │ ├── media_subprocessor.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ ├── nyan_subprocessor.py │ │ │ │ │ ├── processor.py │ │ │ │ │ ├── tech_subprocessor.py │ │ │ │ │ ├── upgrade_attribute_subprocessor.py │ │ │ │ │ └── upgrade_resource_subprocessor.py │ │ │ │ ├── hd/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── media_subprocessor.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ └── processor.py │ │ │ │ ├── ror/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── ability_subprocessor.py │ │ │ │ │ ├── auxiliary_subprocessor.py │ │ │ │ │ ├── civ_subprocessor.py │ │ │ │ │ ├── media_subprocessor.py │ │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ │ ├── nyan_subprocessor.py │ │ │ │ │ ├── pregen_subprocessor.py │ │ │ │ │ ├── processor.py │ │ │ │ │ ├── tech_subprocessor.py │ │ │ │ │ ├── upgrade_ability_subprocessor.py │ │ │ │ │ ├── upgrade_attribute_subprocessor.py │ │ │ │ │ └── upgrade_resource_subprocessor.py │ │ │ │ └── swgbcc/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── ability_subprocessor.py │ │ │ │ ├── auxiliary_subprocessor.py │ │ │ │ ├── civ_subprocessor.py │ │ │ │ ├── modpack_subprocessor.py │ │ │ │ ├── nyan_subprocessor.py │ │ │ │ ├── pregen_subprocessor.py │ │ │ │ ├── processor.py │ │ │ │ ├── tech_subprocessor.py │ │ │ │ ├── upgrade_attribute_subprocessor.py │ │ │ │ └── upgrade_resource_subprocessor.py │ │ │ └── export/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.pxd │ │ │ ├── __init__.py │ │ │ ├── data_exporter.py │ │ │ ├── generate_manifest_hashes.py │ │ │ ├── media_exporter.py │ │ │ ├── modpack_exporter.py │ │ │ ├── terrain_merge.pyx │ │ │ └── texture_merge.pyx │ │ ├── service/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── conversion/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_name_lookups.py │ │ │ ├── debug_info.py │ │ │ ├── export/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── interface/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── cutter.py │ │ │ │ │ ├── rename.py │ │ │ │ │ └── visgrep.pyx │ │ │ │ ├── load_media_cache.py │ │ │ │ ├── opus/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.pxd │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── bytearray.pxd │ │ │ │ │ ├── demo.py │ │ │ │ │ ├── ogg.pxd │ │ │ │ │ ├── opus.pxd │ │ │ │ │ └── opusenc.pyx │ │ │ │ └── png/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.pxd │ │ │ │ ├── __init__.py │ │ │ │ ├── binpack.pxd │ │ │ │ ├── binpack.pyx │ │ │ │ ├── libpng.pxd │ │ │ │ ├── png_create.pyx │ │ │ │ └── png_tmp_file.pxd │ │ │ ├── init/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── api_export_required.py │ │ │ │ ├── changelog.py │ │ │ │ ├── modpack_search.py │ │ │ │ ├── mount_asset_dirs.py │ │ │ │ └── version_detect.py │ │ │ └── read/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── gamedata.py │ │ │ ├── nyan_api_loader.py │ │ │ ├── palette.py │ │ │ ├── register_media.py │ │ │ └── string_resource.py │ │ ├── tool/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── api_export.py │ │ │ ├── driver.py │ │ │ ├── interactive.py │ │ │ ├── singlefile.py │ │ │ └── subtool/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── acquire_sourcedir.py │ │ │ └── version_select.py │ │ └── value_object/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── conversion/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── aoc/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_nyan_names.py │ │ │ ├── de1/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_nyan_names.py │ │ │ ├── de2/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_nyan_names.py │ │ │ ├── forward_ref.py │ │ │ ├── hd/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── ak/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── internal_nyan_names.py │ │ │ │ ├── fgt/ │ │ │ │ │ ├── CMakeLists.txt │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── internal_nyan_names.py │ │ │ │ └── raj/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_nyan_names.py │ │ │ ├── ror/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ └── internal_nyan_names.py │ │ │ └── swgb/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ └── internal_nyan_names.py │ │ ├── init/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── game_file_version.py │ │ │ └── game_version.py │ │ └── read/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── dynamic_loader.py │ │ ├── genie_structure.py │ │ ├── media/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.pxd │ │ │ ├── __init__.py │ │ │ ├── blendomatic.py │ │ │ ├── colortable.py │ │ │ ├── datfile/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── civ.py │ │ │ │ ├── empiresdat.py │ │ │ │ ├── graphic.py │ │ │ │ ├── lookup_dicts.py │ │ │ │ ├── maps.py │ │ │ │ ├── playercolor.py │ │ │ │ ├── research.py │ │ │ │ ├── sound.py │ │ │ │ ├── tech.py │ │ │ │ ├── terrain.py │ │ │ │ └── unit.py │ │ │ ├── drs.py │ │ │ ├── hardcoded/ │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── __init__.py │ │ │ │ ├── interface.py │ │ │ │ ├── termcolors.py │ │ │ │ ├── terrain_tile_size.py │ │ │ │ └── texture.py │ │ │ ├── langcodes.py │ │ │ ├── pefile.py │ │ │ ├── peresource.py │ │ │ ├── sld.pyx │ │ │ ├── slp.pyx │ │ │ ├── smp.pyx │ │ │ └── smx.pyx │ │ ├── media_types.py │ │ ├── member_access.py │ │ ├── read_members.py │ │ └── value_members.py │ ├── cppinterface/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── exctranslate.pyx │ │ ├── exctranslate_tests.pyx │ │ ├── pyobject.pyx │ │ ├── setup.py │ │ ├── setup_checker.pyx │ │ └── typedefs.pxd │ ├── cvar/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── config_file.py │ │ ├── cvar.pyx │ │ └── location.py │ ├── cython_check.pyx │ ├── default_dirs.py │ ├── devmode.py │ ├── event/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ └── demo.pyx │ ├── game/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── main.py │ │ └── main_cpp.pyx │ ├── gamestate/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ └── tests.pyx │ ├── log/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── log_cpp.pyx │ │ └── tests.py │ ├── main/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── main.py │ │ ├── main_cpp.pyx │ │ └── tests.pyx │ ├── nyan/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── import_tree.py │ │ └── nyan_structs.py │ ├── pathfinding/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ └── tests.pyx │ ├── renderer/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── renderer_cpp.pyx │ │ └── tests.pyx │ ├── testing/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── benchmark.py │ │ ├── cpp_testing.pyx │ │ ├── doctest.py │ │ ├── list_processor.py │ │ ├── main.py │ │ ├── misc_cpp.pyx │ │ ├── testing.py │ │ └── testlist.py │ ├── util/ │ │ ├── CMakeLists.txt │ │ ├── __init__.py │ │ ├── bytequeue.py │ │ ├── context.py │ │ ├── decorators.py │ │ ├── dll.py │ │ ├── filelike/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── abstract.py │ │ │ ├── cpp.pyx │ │ │ ├── fifo.py │ │ │ ├── readonly.py │ │ │ └── stream.py │ │ ├── files.py │ │ ├── fslike/ │ │ │ ├── CMakeLists.txt │ │ │ ├── __init__.py │ │ │ ├── abstract.py │ │ │ ├── cpp.pxd │ │ │ ├── cpp.pyx │ │ │ ├── directory.py │ │ │ ├── filecollection.py │ │ │ ├── path.py │ │ │ ├── test.py │ │ │ ├── union.py │ │ │ └── wrapper.py │ │ ├── fsprinting.py │ │ ├── hash.py │ │ ├── iterators.py │ │ ├── math.py │ │ ├── observer.py │ │ ├── ordered_set.py │ │ ├── profiler.py │ │ ├── strings.py │ │ ├── struct.py │ │ ├── system.py │ │ ├── threading.py │ │ └── version.py │ └── versions/ │ ├── CMakeLists.txt │ ├── __init__.py │ └── versions.pyx ├── openage_version ├── packaging/ │ ├── CMakeLists.txt │ ├── CPackOptions.cmake │ └── docker/ │ └── devenv/ │ └── Dockerfile.ubuntu.2404 ├── run.py.in └── shell.nix ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ --- # SFT codestyle # Tab indent + space alignment # see documentation in doc/code_style/ for details and explainations. Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignArrayOfStructures: None AlignConsecutiveAssignments: false AlignConsecutiveBitFields: false AlignConsecutiveDeclarations: false AlignConsecutiveMacros: false AlignEscapedNewlines: DontAlign AlignOperands: Align AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BraceWrapping: AfterCaseLabel: false AfterClass: false AfterControlStatement: Never AfterEnum: false AfterExternBlock: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: true BeforeElse: true BeforeLambdaBody: false BeforeWhile: true IndentBraces: false SplitEmptyFunction: false SplitEmptyNamespace: false SplitEmptyRecord: false BreakAfterJavaFieldAnnotations: true BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakBeforeTernaryOperators: false BreakConstructorInitializers: AfterColon BreakInheritanceList: BeforeComma BreakStringLiterals: false ColumnLimit: 0 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '.*' Priority: 3 SortPriority: 0 IncludeIsMainRegex: '' IncludeIsMainSourceRegex: '' IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent IndentGotoLabels: false IndentPPDirectives: BeforeHash IndentWidth: 4 IndentWrappedFunctionNames: false # clang-format-16 InsertNewlineAtEOF: true InsertTrailingCommas: Wrapped KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 NamespaceIndentation: None PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Right ReflowComments: true SortIncludes: CaseInsensitive SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Never SpacesInCStyleCastParentheses: false SpacesInConditionalStatement: false SpacesInContainerLiterals: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Latest TabWidth: 4 UseCRLF: false UseTab: AlignWithSpaces ... ================================================ FILE: .github/PULL_REQUEST_TEMPLATES/pull_request_template.md ================================================ ### Merge Checklist - [ ] I have read the [contribution guide](doc/contributing.md) - [ ] I have added my info to [copying.md](copying.md) (only first time contributors) - [ ] I have run `make checkmerge` and fixed all mentioned problems ### Description ================================================ FILE: .github/PULL_REQUEST_TEMPLATES/release_template.md ================================================ ### Checklist - [ ] Changelog - [ ] Release date - [ ] Version number in title - [ ] Full commit log - [ ] Bump version in `openage_version` ================================================ FILE: .github/workflows/macosx-ci.yml ================================================ name: macOS-CI on: [push, pull_request] jobs: # https://docs.github.com/en/actions/reference/software-installed-on-github-hosted-runners # we install stuff not already there macos-build: runs-on: macos-latest steps: - name: Checkout sources uses: actions/checkout@v4 # caching dependencies and ccache # https://docs.github.com/en/actions/configuring-and-managing-workflows/caching-dependencies-to-speed-up-workflows - name: Get timestamp id: timestamp shell: cmake -P {0} run: | string(TIMESTAMP current_date "%Y-%m-%d-%H:%M:%S" UTC) message("timestamp=${current_date}" >> $GITHUB_OUTPUT) - name: Cache dependencies uses: actions/cache@v4 id: cache-deps with: path: | ~/Library/Caches/pip ~/Library/Caches/Homebrew key: '${{ runner.os }}-deps-${{ steps.timestamp.outputs.timestamp }}' restore-keys: | ${{ runner.os }}-deps- - name: Cache ccache dir uses: actions/cache@v4 id: cache-ccache with: path: ~/Library/Caches/ccache key: '${{ runner.os }}-ccache-${{ steps.timestamp.outputs.timestamp }}' restore-keys: | ${{ runner.os }}-ccache- - name: Brew update-reset run: brew update-reset - name: Brew update run: brew update - name: Brew install DeJaVu fonts run: brew install --cask font-dejavu - name: Install environment helpers with homebrew run: brew install --force ccache - name: Install LLVM with homebrew run: brew install --force llvm - name: Install openage dependencies with homebrew run: brew install --force cmake python3 libepoxy freetype fontconfig harfbuzz opus opusfile qt6 libogg libpng toml11 eigen - name: Install nyan dependencies with homebrew run: brew install --force flex make - name: Install python3 packages # cython, numpy and pygments are in homebrew, # but "cython is keg-only, which means it was not symlinked into /usr/local" # numpy pulls gcc as dep? and pygments doesn't work. run: pip3 install --upgrade --break-system-packages cython numpy mako lz4 pillow pygments setuptools toml - name: Configure run: ./configure --compiler="$(brew --prefix llvm)/bin/clang++" --mode=release --ccache --download-nyan - name: Build run: make -j$(sysctl -n hw.logicalcpu) VERBOSE=1 - name: Test run: make test ================================================ FILE: .github/workflows/ubuntu-24.04.yml ================================================ name: Ubuntu 24.04 CI on: [push, workflow_dispatch] jobs: build-devenv: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Build the Docker image run: sudo DOCKER_BUILDKIT=1 docker build ./packaging/docker/devenv --file ./packaging/docker/devenv/Dockerfile.ubuntu.2404 --tag openage-devenv:latest shell: bash - name: Save the Docker image run: | mkdir -p /tmp/staging sudo docker save openage-devenv:latest | gzip > /tmp/staging/devenv.tar.gz shell: bash - name: Publish the Docker image uses: actions/upload-artifact@v4 with: name: devenv-image-compressed.tar.gz path: '/tmp/staging/devenv.tar.gz' if-no-files-found: error retention-days: 30 build: runs-on: ubuntu-24.04 needs: build-devenv steps: - uses: actions/checkout@v4 - name: Create tmp path run: mkdir -p /tmp/image shell: bash - name: Download devenv image uses: actions/download-artifact@v4 with: name: devenv-image-compressed.tar.gz path: '/tmp/image' - name: Load Docker image run: sudo docker load --input /tmp/image/devenv.tar.gz - name: Build openage run: | sudo docker run --rm -v "$(pwd)":/mnt/openage -w /mnt/openage openage-devenv:latest \ bash -c 'mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=$(which gcc) -DCMAKE_CXX_COMPILER=$(which g++) -DCMAKE_CXX_FLAGS='' -DCMAKE_EXE_LINKER_FLAGS='' -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_MODULE_LINKER_FLAGS='' -DCMAKE_SHARED_LINKER_FLAGS='' -DDOWNLOAD_NYAN=YES -DCXX_OPTIMIZATION_LEVEL=auto -DCXX_SANITIZE_FATAL=False -DCXX_SANITIZE_MODE=none -DWANT_BACKTRACE=if_available -DWANT_GPERFTOOLS_PROFILER=if_available -DWANT_GPERFTOOLS_TCMALLOC=False -DWANT_INOTIFY=if_available -DWANT_NCURSES=if_available -DWANT_OPENGL=if_available -DWANT_VULKAN=if_available -G Ninja .. && cmake --build . --parallel $(nproc) -- -k1' - name: Compress build artifacts run: | mkdir -p /tmp/openage tar -czvf /tmp/openage/openage-build.tar.gz ./build - name: Publish build artifacts uses: actions/upload-artifact@v4 with: name: openage-build.tar.gz path: '/tmp/openage/openage-build.tar.gz' if-no-files-found: error retention-days: 30 ================================================ FILE: .github/workflows/windows-server-2019.yml ================================================ name: Windows Server 2019 CI on: [push, workflow_dispatch] jobs: build-x64: runs-on: windows-2019 steps: - uses: actions/checkout@v4 with: submodules: recursive path: source - name: Inspect environment run: | vswhere -latest shell: pwsh - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.9' architecture: 'x64' - name: Install dependencies [vcpkg] run: | mkdir download cd download $zipfile = "openage-dep-x64-windows.zip" Invoke-WebRequest https://github.com/SFTtech/openage-dependencies/releases/download/v0.5.1/openage-dep-x64-windows.zip -OutFile $zipfile Expand-Archive -Path $zipfile -DestinationPath . -Force Remove-Item $zipfile (Get-ChildItem . -Recurse -File).FullName shell: pwsh - name: Install dependencies [winflexbison] run: | cd download $zipfile = "winflexbison-2.5.24.zip" Invoke-WebRequest https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip -OutFile $zipfile mkdir winflexbison Expand-Archive -Path $zipfile -DestinationPath ./winflexbison -Force Remove-Item $zipfile (Get-ChildItem ./winflexbison -Recurse -File).FullName shell: pwsh - name: Install dependencies [Python] run: | python -m pip install --upgrade pip python -m pip install --upgrade Cython wheel numpy lz4 toml pillow pygments pyreadline3 mako shell: pwsh - name: Build run: | $TOOLCHAIN_FILE = Join-Path download openage-dep-x64-windows scripts buildsystems vcpkg.cmake | Resolve-Path $FLEX_PATH = (Get-ChildItem ./download -Recurse -Force -Filter 'win_flex.exe')[0].FullName mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TRY_COMPILE_CONFIGURATION=Debug -DCMAKE_CXX_FLAGS='/Zc:__cplusplus /permissive- /EHsc' -DCMAKE_EXE_LINKER_FLAGS='' -DCMAKE_MODULE_LINKER_FLAGS='' -DCMAKE_SHARED_LINKER_FLAGS='' -DDOWNLOAD_NYAN=YES -DCXX_OPTIMIZATION_LEVEL=auto -DCXX_SANITIZE_FATAL=False -DCXX_SANITIZE_MODE=none -DWANT_BACKTRACE=if_available -DWANT_GPERFTOOLS_PROFILER=if_available -DWANT_GPERFTOOLS_TCMALLOC=False -DWANT_INOTIFY=if_available -DWANT_NCURSES=if_available -DWANT_OPENGL=if_available -DWANT_VULKAN=if_available -DFLEX_EXECUTABLE="$FLEX_PATH" -G "Visual Studio 16 2019" -A x64 ../source cmake --build . --config Debug -- -nologo -maxCpuCount shell: pwsh - name: Package run: | mkdir package cd package mkdir dll cd .. $STAGING_PATH = Resolve-Path package $DLL_PATH = Join-Path package dll | Resolve-Path cd build $NYAN_DLL = (Get-ChildItem . -Recurse -Force -Filter 'nyan.dll')[0].FullName $OPENAGE_DLL = (Get-ChildItem . -Recurse -Force -Filter 'openage.dll')[0].FullName $NATIVE_OUTPUT = Split-Path -Path $OPENAGE_DLL -Parent Copy-Item -Path ./openage -Destination $STAGING_PATH -Recurse Copy-Item -Path $NYAN_DLL -Destination $DLL_PATH Copy-Item -Path (Join-Path $NATIVE_OUTPUT *.dll) -Destination $DLL_PATH Copy-Item -Path run.* -Destination $STAGING_PATH shell: pwsh - name: Test run: | $DLL_PATH = Join-Path package dll | Resolve-Path cd package python -m openage --add-dll-search-path $DLL_PATH --version python -m openage --add-dll-search-path $DLL_PATH test -a shell: pwsh - name: Publish build artifacts uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: build-files path: './build' if-no-files-found: error retention-days: 30 - name: Publish packaged artifacts uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: package-files path: './package' if-no-files-found: error retention-days: 30 ================================================ FILE: .github/workflows/windows-server-2022.yml ================================================ name: Windows Server 2022 CI on: [push, workflow_dispatch] jobs: build-x64: runs-on: windows-2022 steps: - uses: actions/checkout@v4 with: submodules: recursive path: source - name: Inspect environment run: | vswhere -latest shell: pwsh - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.9' architecture: 'x64' - name: Install dependencies [vcpkg] run: | mkdir download cd download $zipfile = "openage-dep-x64-windows.zip" Invoke-WebRequest https://github.com/SFTtech/openage-dependencies/releases/download/v0.5.1/openage-dep-x64-windows.zip -OutFile $zipfile Expand-Archive -Path $zipfile -DestinationPath . -Force Remove-Item $zipfile (Get-ChildItem . -Recurse -File).FullName shell: pwsh - name: Install dependencies [winflexbison] run: | cd download $zipfile = "winflexbison-2.5.24.zip" Invoke-WebRequest https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip -OutFile $zipfile mkdir winflexbison Expand-Archive -Path $zipfile -DestinationPath ./winflexbison -Force Remove-Item $zipfile (Get-ChildItem ./winflexbison -Recurse -File).FullName shell: pwsh - name: Install dependencies [Python] run: | python -m pip install --upgrade pip python -m pip install --upgrade Cython wheel numpy lz4 toml pillow pygments pyreadline3 mako shell: pwsh - name: Build run: | $TOOLCHAIN_FILE = Join-Path download openage-dep-x64-windows scripts buildsystems vcpkg.cmake | Resolve-Path $FLEX_PATH = (Get-ChildItem ./download -Recurse -Force -Filter 'win_flex.exe')[0].FullName mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TRY_COMPILE_CONFIGURATION=Debug -DCMAKE_CXX_FLAGS='/Zc:__cplusplus /permissive- /EHsc' -DCMAKE_EXE_LINKER_FLAGS='' -DCMAKE_MODULE_LINKER_FLAGS='' -DCMAKE_SHARED_LINKER_FLAGS='' -DDOWNLOAD_NYAN=YES -DCXX_OPTIMIZATION_LEVEL=auto -DCXX_SANITIZE_FATAL=False -DCXX_SANITIZE_MODE=none -DWANT_BACKTRACE=if_available -DWANT_GPERFTOOLS_PROFILER=if_available -DWANT_GPERFTOOLS_TCMALLOC=False -DWANT_INOTIFY=if_available -DWANT_NCURSES=if_available -DWANT_OPENGL=if_available -DWANT_VULKAN=if_available -DFLEX_EXECUTABLE="$FLEX_PATH" -G "Visual Studio 17 2022" -A x64 ../source cmake --build . --config Debug -- -nologo -maxCpuCount shell: pwsh - name: Package run: | mkdir package cd package mkdir dll cd .. $STAGING_PATH = Resolve-Path package $DLL_PATH = Join-Path package dll | Resolve-Path cd build $NYAN_DLL = (Get-ChildItem . -Recurse -Force -Filter 'nyan.dll')[0].FullName $OPENAGE_DLL = (Get-ChildItem . -Recurse -Force -Filter 'openage.dll')[0].FullName $NATIVE_OUTPUT = Split-Path -Path $OPENAGE_DLL -Parent Copy-Item -Path ./openage -Destination $STAGING_PATH -Recurse Copy-Item -Path $NYAN_DLL -Destination $DLL_PATH Copy-Item -Path (Join-Path $NATIVE_OUTPUT *.dll) -Destination $DLL_PATH Copy-Item -Path run.* -Destination $STAGING_PATH shell: pwsh - name: Test run: | $DLL_PATH = Join-Path package dll | Resolve-Path cd package python -m openage --add-dll-search-path $DLL_PATH --version python -m openage --add-dll-search-path $DLL_PATH test -a shell: pwsh - name: Publish build artifacts uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: build-files path: './build' if-no-files-found: error retention-days: 30 - name: Publish packaged artifacts uses: actions/upload-artifact@v4 if: ${{ always() }} with: name: package-files path: './package' if-no-files-found: error retention-days: 30 ================================================ FILE: .gitignore ================================================ # editor-specific files \#* .#* *~ .*.swp /CMakeLists.txt.user *.temp # ELF files *.o *.a *.so # python bytecode *.pyc *.pyo __pycache__ *.pyd # MSVC files *.pdb *.dll *.exe # workflow *.orig /TODO.org # build system /bin /.bin /build /deps /.ccls-cache /.cache # Nix build output /result # root dir run script /run /run.cpp /run.html # CMake in-source builds /DartConfiguration.tcl /codegen_depend_cache /codegen_target_cache /Doxyfile /Testing /py/setup.py CTestTestfile.cmake CMakeFiles cmake_install.cmake CMakeCache.txt Makefile !/Makefile # debugging callgrind.out.* perf.data* .gdb_history # code search /.ignore /.globalrc /GPATH /GRTAGS /GTAGS # IDEs .vscode .idea # CMake + ccls /compile_commands.json # Virtual Environments /.venv/ # macOS .DS_Store # copyrighted assets /assets/converted/ /assets/terrain/ ================================================ FILE: .mailmap ================================================ Michael Enßlin Jonas Jelten Jonas Jelten Jonas Jelten Renée Kooi Renée Kooi Ingo Saftbaumer Ingo Saftbaumer James Mintram Sam Schetterer Jimmy Berry Jonathan Remnant Henry Snoek coop shell (Michael Enßlin, Jonas Jelten, Andre Kupka) Franz-Niclas Muschter Niklas Fiekas Wojciech Nawrocki Simon San <14062932+simonsan@users.noreply.github.com> Tobias Feldballe Tobias Feldballe Jonas Borchelt Derek Frogget <114030121+derekfrogget@users.noreply.github.com> Nikhil Ghosh David Wever <56411717+dmwever@users.noreply.github.com> Ngô Xuân Minh ================================================ FILE: CMakeLists.txt ================================================ # Copyright 2013-2023 the openage authors. See copying.md for legal info. # >=3.16 finding numpy with the findpython3 module cmake_minimum_required(VERSION 3.16) # main build configuration file # text art: figlet -f rounded "[SFT] openage" | sed -e 's/\\/\\\\/g' message(" ___ ______ _______ _______ ___ | _)/ _____|_______|_______|_ | | | ( (____ _____ _ | | ___ ____ _____ ____ _____ ____ _____ | | \\____ \\| ___) | | | | / _ \\| _ \\| ___ | _ \\(____ |/ _ | ___ | | |_ _____) ) | | | _| | | |_| | |_| | ____| | | / ___ ( (_| | ____| |___|______/|_| |_| (___| \\___/| __/|_____)_| |_\\_____|\\___ |_____) |_| (_____| Welcome to the SFT technologies computer-aided openage build system! You have chosen, or been chosen, to attempt the daring task of building openage. If you have installed all the dependencies that are conveniently listed in [doc/building.md], this _might_ just work! If it doesn't, consider reporting the issue, or ask for help: * GitHub: https://github.com/SFTtech/openage * Matrix: #sfttech:matrix.org ") ################################################## # main buildsystem setup entry point project(openage CXX) # C++ standard requirement set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Python and Cython requirements set(PYTHON_MIN_VERSION 3.9) set(CYTHON_MIN_VERSION 3.0.10) set(CYTHON_MIN_VERSION_FALLBACK 0.29.31) set(CYTHON_MAX_VERSION_FALLBACK 3.0.7) # CMake policies foreach(pol CMP0074 # use _ROOT vars in find_package() CMP0067 # honor language standard in try_compile() CMP0071 # enable automoc for generated files CMP0072 # prefers GLVND by default FindOpenGL CMP0048 # project() command manages VERSION variables CMP0094 # take the first satisfying Python version CMP0082 # run add_subdirectory() in the declaration order CMP0102 # Don't create empty cache entries ) if (POLICY ${pol}) cmake_policy(SET ${pol} NEW) endif() endforeach() # don't print 'Built target ...' messages # upstream since cmake v3.4.0-rc1 (by commit 1d3984780df8) set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF) # Ensure CMAKE_BUILD_TYPE is set correctly. if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() string(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" BUILD_TYPE_CXX_FLAGS) ################################################## # options: keep up to date with those in ./configure! if(NOT DEFINED WANT_BACKTRACE) set(WANT_BACKTRACE if_available) endif() if(NOT DEFINED WANT_INOTIFY) set(WANT_INOTIFY if_available) endif() if(NOT DEFINED WANT_OPENGL) set(WANT_OPENGL if_available) endif() if(NOT DEFINED WANT_VULKAN) set(WANT_VULKAN if_available) endif() if(NOT DEFINED WANT_GPERFTOOLS_PROFILER) set(WANT_GPERFTOOLS_PROFILER if_available) endif() if(NOT DEFINED WANT_GPERFTOOLS_TCMALLOC) set(WANT_GPERFTOOLS_TCMALLOC false) endif() if(NOT DEFINED WANT_NCURSES) set(WANT_NCURSES if_available) endif() if(NOT DEFINED WANT_IWYU) set(WANT_IWYU false) endif() ################################################## # static content filesystem locations if(NOT DEFINED GLOBAL_ASSET_DIR) set(ASSET_DIR "share/openage") if(MSVC) set(GLOBAL_ASSET_DIR "${ASSET_DIR}") else() set(GLOBAL_ASSET_DIR "${CMAKE_INSTALL_PREFIX}/${ASSET_DIR}") endif() endif() if(NOT DEFINED GLOBAL_CONFIG_DIR) set(CONFIG_DIR "etc/openage") if(MSVC) set(GLOBAL_CONFIG_DIR "${CONFIG_DIR}") else() set(GLOBAL_CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/${CONFIG_DIR}") endif() endif() ################################################## # ccache setup # distros can also do this but they don't use this mechanism option(ENABLE_CCACHE "prefix each compile command with ccache") if(ENABLE_CCACHE) find_program(CCACHE_FOUND "ccache") if(CCACHE_FOUND) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) else() message(FATAL_ERROR "ccache not found, but you requested it") endif(CCACHE_FOUND) endif() ################################################## # clang tidy static analysis option( ENABLE_CLANG_TIDY "activate clang tidy messages" OFF ) if(ENABLE_CLANG_TIDY) set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=-*,readability-*") endif() # option processing is now done. ################################################## # include buildsystem features # add search paths to helper modules set(BUILDSYSTEM_DIR "${CMAKE_SOURCE_DIR}/buildsystem") set(CMAKE_MODULE_PATH "${BUILDSYSTEM_DIR}" "${BUILDSYSTEM_DIR}/modules/") # prioritize macOS frameworks since they're probably newer # than the system libraries set(CMAKE_FIND_FRAMEWORK LAST) set(CMAKE_FIND_APPBUNDLE LAST) # load helper modules include(GNUInstallDirs) include(CheckInSourceBuild) include(HandleCXXOptions) include(CheckCompilerFeatures) include(CMakeParseArguments) include(HandlePythonOptions) include(CheckRuntimeDependencies) include(DetectProjectVersion) include(DependencyFetch) include(FindPackageHandleStandardArgs) # include build configuration modules include(CTest) # initialize language support include(codegen) include(cpp) include(doxygen) include(options) include(python) include(util) # now that all modules and settings are loaded, # apply those to the project. ################################################## # set project version if(USED_GIT_VERSION) # VERSION_FULL_STRING is the full git describe set(VERSION_FULL_STRING "${PROJECT_VERSION}") # PROJECT_VERSION is MAJOR.MINOR.PATCH.TWEAK with Commit-Count as Tweak STRING(REGEX REPLACE "v(([0-9]+.|[0-9]+)+)-([0-9]+)-g([a-f0-9]+)" "\\1.\\3" PROJECT_VERSION "${VERSION_FULL_STRING}") endif() project(openage VERSION "${PROJECT_VERSION}") # set CI version if(DEFINED ENV{CI_CFG_VERSION}) set(CI_CFG_VERSION "$ENV{CI_CFG_VERSION}") else() set(CI_CFG_VERSION "NOT SET") endif() ################################################## # documentation generation # create documentation doxygen_configure(libopenage/ openage/ doc/ README.md) ################################################## # static content add_subdirectory(assets/) add_subdirectory(dist/) add_subdirectory(cfg/) ################################################## # C++ content add_subdirectory(libopenage/) ################################################## # Python content (uses the C++ library) # create a virtual library that, when linked to, # injects a header inclusion, and links to libopenage. # -> all cython modules get our hacks included and link to libopenage. add_library(pyext_libopenage INTERFACE) if(MSVC) set(FORCE_INCLUDE_CXXFLAG "/FI") else() set(FORCE_INCLUDE_CXXFLAG "-include") endif() target_compile_options(pyext_libopenage INTERFACE ${FORCE_INCLUDE_CXXFLAG} "${CMAKE_SOURCE_DIR}/libopenage/pyinterface/hacks.h" ) target_link_libraries(pyext_libopenage INTERFACE libopenage) set(PYEXT_LINK_LIBRARY pyext_libopenage) configure_file(run.py.in run.py) add_cython_modules(EMBED NOINSTALL ${CMAKE_CURRENT_BINARY_DIR}/run.py) add_py_modules(BININSTALL ${CMAKE_CURRENT_BINARY_DIR}/run.py AS openage) add_subdirectory(openage/) python_finalize() ################################################## # packaging. # Ensure that packaging is always the last step. add_subdirectory(packaging) ################################################## # show build configuration overview message("") print_config_options() message("${PROJECT_NAME} ${PROJECT_VERSION} version string | ${VERSION_FULL_STRING} compiler | ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} python | ${PYTHON_VERSION_STRING} build type | ${CMAKE_BUILD_TYPE} cxxflags | ${CMAKE_CXX_FLAGS} ${EXTRA_FLAGS} build type flags | ${${BUILD_TYPE_CXX_FLAGS}} build dir | ${CMAKE_BINARY_DIR} install prefix | ${CMAKE_INSTALL_PREFIX} py install prefix | ${CMAKE_PY_INSTALL_PREFIX} ") ################################################## # done! that was easy, right? ================================================ FILE: Makefile ================================================ # type 'make help' for a list/explaination of recipes. BUILDDIR = bin MAKEARGS += $(if $(VERBOSE),,--no-print-directory) .PHONY: default default: build .PHONY: all all: default $(BUILDDIR): @echo "call ./configure to initialize the build directory." @echo "also see ./configure --help, and doc/building.md" @echo "" @false .PHONY: install install: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) install .PHONY: run run: build cd $(BUILDDIR) && ./run main .PHONY: test test: tests checkfast .PHONY: tests tests: build cd $(BUILDDIR) && ./run test -a .PHONY: build build: $(BUILDDIR) @$(MAKE) $(MAKEARGS) -C $(BUILDDIR) .PHONY: ninja ninja: $(BUILDDIR) @ninja -C $(BUILDDIR) .PHONY: libopenage libopenage: $(BUILDDIR) @$(MAKE) $(MAKEARGS) -C $(BUILDDIR) libopenage .PHONY: codegen codegen: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cppgen .PHONY: cppgen cppgen: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cppgen .PHONY: pxdgen pxdgen: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) pxdgen .PHONY: compilepy compilepy: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) compilepy .PHONY: inplacemodules inplacemodules: $(MAKE) $(MAKEARGS) -C $(BUILDDIR) inplacemodules .PHONY: cythonize cythonize: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cythonize .PHONY: doc doc: $(BUILDDIR) $(MAKE) $(MAKEARGS) -C $(BUILDDIR) doc .PHONY: cleanelf cleanelf: $(BUILDDIR) @# removes all object files and binaries $(MAKE) $(MAKEARGS) -C $(BUILDDIR) clean .PHONY: cleancodegen cleancodegen: $(BUILDDIR) @# removes all sourcefiles created by codegen $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cleancodegen .PHONY: cleanpxdgen cleanpxdgen: $(BUILDDIR) @# removes all generated .pxd files $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cleanpxdgen .PHONY: cleancython cleancython: $(BUILDDIR) @# removes all .cpp files created by Cython $(MAKE) $(MAKEARGS) -C $(BUILDDIR) cleancython .PHONY: clean clean: $(BUILDDIR) cleancodegen cleanpxdgen cleancython cleanelf @# removes object files, binaries, py modules, generated code .PHONY: cleaninsourcebuild cleaninsourcebuild: @echo "cleaning remains of in-source builds" rm -rf DartConfiguration.tcl codegen_depend_cache codegen_target_cache Doxyfile Testing @find . -not -path "./.bin/*" -type f -name CTestTestfile.cmake -print -delete @find . -not -path "./.bin/*" -type f -name cmake_install.cmake -print -delete @find . -not -path "./.bin/*" -type f -name CMakeCache.txt -print -delete @find . -not -path "./.bin/*" -type f -name Makefile -not -path "./Makefile" -print -delete @find . -not -path "./.bin/*" -type d -name CMakeFiles -print -exec rm -r {} + .PHONY: cleanbuilddirs cleanbuilddirs: cleaninsourcebuild @if test -d bin; then $(MAKE) $(MAKEARGS) -C bin clean cleancython cleanpxdgen cleancodegen || true; fi @echo cleaning symlinks to build directories rm -f bin @echo cleaning build directories rm -rf .bin @echo cleaning cmake-time generated code rm -f Doxyfile py/openage/config.py libopenage/config.h libopenage/config.cpp .PHONY: mrproper mrproper: cleanbuilddirs @echo cleaning converted assets rm -rf userassets .PHONY: mrproperer mrproperer: mrproper @if ! test -d .git; then echo "mrproperer is only available for gitrepos."; false; fi @echo removing ANYTHING that is not checked into the git repo @echo ENTER to confirm @read val git clean -x -d -f .PHONY: checkfast checkfast: python3 -m buildsystem.codecompliance --fast .PHONY: checkmerge checkmerge: python3 -m buildsystem.codecompliance --merge .PHONY: checkchanged checkchanged: python3 -m buildsystem.codecompliance --merge --only-changed-files=origin/master .PHONY: checkuncommited checkuncommited: python3 -m buildsystem.codecompliance --merge --only-changed-files=HEAD .PHONY: checkpy checkpy: python3 -m buildsystem.codecompliance --pystyle --pylint .PHONY: checkall checkall: python3 -m buildsystem.codecompliance --all .PHONY: help help: $(BUILDDIR)/Makefile @echo "openage Makefile" @echo "" @echo "wrapper that mostly forwards recipes to the cmake-generated Makefile in bin/" @echo "" @echo "targets:" @echo "" @echo "build -> build entire project" @echo "libopenage -> build libopenage" @echo "pxdgen -> generate .pxd files" @echo "cythonize -> compile .pyx files to .cpp" @echo "compilepy -> compile .py files to .pyc" @echo "inplacemodules -> create in-place modules" @echo "codegen -> generate cpp sources" @echo "doc -> create documentation files" @echo "" @echo "cleanelf -> remove C++ ELF files" @echo "cleancodegen -> undo 'make codegen'" @echo "cleancython -> undo 'make cythonize inplacemodules'" @echo "cleanpxdgen -> undo 'make pxdgen'" @echo "clean -> undo 'make' (all of the above)" @echo "cleanbuilddirs -> undo 'make' and './configure'" @echo "cleaninsourcebuild -> undo in-source build accidents" @echo "mrproper -> as above, but additionally delete user assets" @echo "mrproperer -> leaves nothing but ashes" @echo "" @echo "run -> run openage" @echo "tests -> run the tests (py + cpp)" @echo "" @echo "checkall -> full code compliance check" @echo "checkmerge -> code compliance check for merging to master" @echo "checkfast -> fast checks only" @echo "checkchanged -> full check for all files changed since origin/master" @echo "checkuncommited -> full check for all currently uncommited files" @echo "checkpy -> check python compliance" @echo "" @echo "test -> tests + checkfast. this is what you should use for regular devbuilds" @echo "" @echo "CMake help:" @test -d $(BUILDDIR) && $(MAKE) -C $(BUILDDIR) help || echo "no builddir is configured" ================================================ FILE: README.md ================================================ [![openage](/assets/logo/banner.svg)](http://openage.dev) ========================================================= **openage**: a volunteer project to create a free engine clone of the *Genie Engine* used by *Age of Empires*, *Age of Empires II (HD)* and *Star Wars: Galactic Battlegrounds*, comparable to projects like [OpenMW](https://openmw.org/), [OpenRA](http://openra.net/), [OpenSAGE](https://github.com/OpenSAGE/OpenSAGE/), [OpenTTD](https://openttd.org/) and [OpenRCT2](https://openrct2.org/). openage uses the original game assets (such as sounds and graphics), but (for obvious reasons) doesn't ship them. To play, you require *[any of the original games (AoE1, AoE2)](/doc/media_convert.md)* or their *Definitive Edition* releases. [![github stars](https://img.shields.io/github/stars/SFTtech/openage.svg)](https://github.com/SFTtech/openage/stargazers) [![#sfttech on matrix.org](/assets/doc/matrixroom.svg)](https://matrix.to/#/#sfttech:matrix.org) [![GPL licensed](/assets/doc/license.svg)](/legal/GPLv3) Contact ------- | Contact | Where? | | ---------------- | -------------------------------------------------------------------------------------------------- | | Issue Tracker | [GitHub SFTtech/openage] | | Development Blog | [blog.openage.dev] | | Subreddit | [![reddit](/assets/doc/reddit.svg) /r/openage](https://www.reddit.com/r/openage/) | | Discussions | [GitHub Discussions] | | Matrix Chat | [![matrix](/assets/doc/matrix.svg) `#sfttech:matrix.org`](https://matrix.to/#/#sfttech:matrix.org) | | Money Sink | [![money sink](/assets/doc/liberapay.svg)](https://liberapay.com/SFTtech) | [GitHub SFTtech/openage]: https://github.com/SFTtech/openage/issues [blog.openage.dev]: https://blog.openage.dev [GitHub Discussions]: https://github.com/SFTtech/openage/discussions Technical foundation -------------------- | Technology | Component | | ------------ | ------------------------------------------------------------- | | **C++20** | Engine core | | **Python3** | Scripting, media conversion, in-game console, code generation | | [**Cython**] | Python/C++ Glue code | | [**Qt6**] | Graphical user interface | | [**CMake**] | Build system | | [**OpenGL**] | Rendering, shaders | | [**Opus**] | Audio codec | | [**nyan**] | Content Configuration and Modding | | [**Humans**] | Mixing together all of the above | [**Cython**]: https://cython.org/ [**Qt6**]: https://contribute.qt-project.org/ [**CMake**]: https://cmake.org/ [**OpenGL**]: https://www.opengl.org/ [**Opus**]: https://opus-codec.org/ [**nyan**]: https://github.com/SFTtech/nyan [**Humans**]: https://www.youtube.com/watch?v=fQGbXmkSArs&t=18s Goals ----- * Fully authentic look and feel * This can only be approximated since the behavior of the original game is mostly undocumented, and guessing/experimenting can only get you this close * We will not implement useless artificial limitations (max 30 selectable units...) * An easily-moddable content format: [**nyan** yet another notation](https://github.com/SFTtech/nyan) * An integrated Python console and API, comparable to [blender](https://www.blender.org/) * AI scripting in Python, you can use [machine learning](http://scikit-learn.org/stable/) * here is some [additional literature](http://www.deeplearningbook.org/) * Re-creating [free game assets](https://github.com/SFTtech/openage-data) * Multiplayer (obviously) * Matchmaking and ranking with a [haskell masterserver](https://github.com/SFTtech/openage-masterserver) * Optionally, [improvements](/doc/ideas/) over the original game * Awesome infrastructure such as our own [Kevin CI service](https://github.com/SFTtech/kevin) But beware, for sanity reasons: * No network compatibility with the original game. You really wanna have the same problems again? * No binary compatibility with the original game. A one-way script to convert maps/savegames/missions to openage is planned though. Current State of the Project ---------------------------- **Important notice**: At the moment, "gameplay" is basically non-functional. We're implementing the internal game simulation (how units even do anything) with simplicity and extensibility in mind, so we had to get rid of the temporary (but kind of working) previous version. With these changes, we can (finally) actually make use of our converted asset packs and our nyan API! We're working day and night to make gameplay return\*. If you're interested, we wrote detailed explanations on our blog: [Part 1](https://blog.openage.dev/new-gamestate-2020.html), [Part 2](https://blog.openage.dev/engine-core-modules.html), [Monthly Devlog](https://blog.openage.dev/tag/news.html). *\* may not actually be every day and night* | Operating System | Build status | | :-----------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | Debian Sid | [![Kevin CI status](https://cidata.sft.lol/openage/branches/master/status.svg)](/kevinfile) | | Ubuntu 24.04 LTS | [![Ubuntu 24.04 build status](https://github.com/SFTTech/openage/actions/workflows/ubuntu-24.04.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/ubuntu-24.04.yml) | | macOS | [![macOS build status](https://github.com/SFTtech/openage/workflows/macOS-CI/badge.svg)](https://github.com/SFTtech/openage/actions?query=workflow%3AmacOS-CI) | | Windows Server 2019 | [![Windows Server 2019 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2019.yml) | | Windows Server 2022 | [![Windows Server 2022 build status](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml/badge.svg?branch=master)](https://github.com/SFTtech/openage/actions/workflows/windows-server-2022.yml) | [Todo: Kevin #11]: https://github.com/SFTtech/kevin/issues/11 Installation Packages --------------------- There are many missing parts for an actually working game. So if you "just wanna play", [you'll be disappointed](#current-state-of-the-project), unfortunately. We strongly recommend building the program from source to get the latest, greatest, and shiniest project state :) * For **Linux** check at [repology](https://repology.org/project/openage/versions) if your distribution has any packages available. Otherwise, you need to build from source. We don't release `*.deb`, `*.rpm`, Flatpak, snap or AppImage packages yet. * For **Windows** check our [release page](https://github.com/SFTtech/openage/releases) for the latest installer. Otherwise, you need to build from the source. * For **macOS** we currently don't have any packages, you need to build from source. If you need help, maybe our [troubleshooting guide](/doc/troubleshooting.md) helps you. Quickstart ---------- * **How do I get this to run on my box?** 1. [Clone](https://docs.github.com/repositories/creating-and-managing-repositories/cloning-a-repository) the repo. 2. Install dependencies. See [doc/building.md](/doc/building.md#dependency-installation) to get instructions for your favorite platform. 3. Build the project: ``` ./configure --download-nyan make ``` **Alternative approach:** You can build and run the project using Docker. See [Running with docker](/doc/build_instructions/docker.md) for more details. * **I compiled everything. Now how do I run it?** * Execute `cd bin && ./run main`. * [The convert script](/doc/media_convert.md) will transform original assets into openage formats, which are a lot saner and more moddable. * Use your brain and react to the things you'll see. * **Waaaaaah! It...** * segfaults * prints error messages I don't want to read * ate my dog All of those are features, not bugs. To turn them off, use `./bin/run --dont-segfault --no-errors --dont-eat-dog`. If this still does not help, try our [troubleshooting guide](/doc/troubleshooting.md), the [contact section](#contact) or the [bug tracker](https://github.com/SFTtech/openage/issues). Contributing ============ You might ask yourself now "Sounds cool, but how do I participate and ~~get famous~~ contribute useful features?". Fortunately for you, there is a lot to do and we are very grateful for your help. ## Where do I start? * **Check the issues** [labelled with `good first issue`](https://github.com/SFTtech/openage/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). These are tasks that you can start right away and don't require much previous knowledge. * **Ask us** in the [chat](https://matrix.to/#/#sfttech:matrix.org). Someone there could need help with something. * You can also **take the initiative** and fix a bug you found, create an issue for discussion or implement a feature that we never thought of, but always wanted. ## Ok, I found something. What now? * **[Tell us](#contact)**, if you haven't already. Chances are that we have additional information and directions. * **[Read the docs](/doc)**. They will answer most "administrative" questions like what code style is used and how the engine core parts are connected. * **Read the code** and get familiar with the engine component you want to work with. * Do not hesitate to **[ask us for help](#contact)** if you do not understand something. ## How do I contribute my features/changes? * Read the **[contributing guide](/doc/contributing.md)**. * You can upload work-in-progress (WIP) versions or drafts of your contribution to get feedback or support. * Tell us (again) when you want us to review your work. ## I want to help, but I'm not a programmer... Then openage might be a good reason to become one! We have many issues and tasks for beginners. You just have to ask and we'll find something. Alternatively, lurking is also allowed. ---- Cheers, happy hecking! Development Process ------------------- What does openage development look like in practice? * extensive [synchronization](#contact)! * [doc/development.md](/doc/development.md). How can I help? * [doc/contributing.md](/doc/contributing.md). All documentation is also in this repo: * Code documentation is embedded in the sources for Doxygen (see [doc readme](/doc/README.md)). * Have a look at the [doc directory](/doc/). This folder tends to get outdated when code changes. License ------- **GNU GPLv3** or later; see [copying.md](copying.md) and [legal/GPLv3](/legal/GPLv3). I know that probably nobody is ever gonna look at the `copying.md` file, but if you want to contribute code to openage, please take the time to skim through it and add yourself to the authors list. ================================================ FILE: assets/.gitignore ================================================ # converted game assets /converted/ ================================================ FILE: assets/CMakeLists.txt ================================================ # Copyright 2014-2017 the openage authors. See copying.md for legal info. add_subdirectory(logo/) add_subdirectory(shaders/) add_subdirectory(test/) add_subdirectory(textures/) install(DIRECTORY "qml" DESTINATION "${ASSET_DIR}" ) # To show QML files in the QtCreator. file(GLOB QML_SRC "qml/*.qml") add_custom_target(qtcreator-show-qml SOURCES ${QML_SRC}) ================================================ FILE: assets/logo/CMakeLists.txt ================================================ install( FILES "crown.svg" RENAME "openage.svg" DESTINATION "share/pixmaps/" ) install( FILES "banner.svg" DESTINATION "${ASSET_DIR}" ) ================================================ FILE: assets/qml/.gitignore ================================================ *.qmlc ================================================ FILE: assets/qml/Actions.qml ================================================ // Copyright 2016-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import yay.sfttech.openage 1.0 as OA Item { id: root /* * Calls 'actionMode.act(name)' to execute actions. */ property var actionMode /* * Use "act" properties of these objects to access the Action. * Action has additional "iconCheckedSource" property. */ property alias actionObjects: actionObjectList.children visible: false ExclusiveGroup { id: stanceGrp } ExclusiveGroup { id: noGrp } Item { id: actionObjectList Repeater { model: OA.ActionsListModel { id: actionObjectListModel action_mode: root.actionMode } delegate: Item { property QtObject act: Action { onTriggered: root.actionMode.act(name) iconSource: ico != -1 ? actionObjectListModel.iconsSource + "." + ico : "" property url iconCheckedSource: icoChk != -1 ? actionObjectListModel.iconsSource + "." + icoChk : "" checkable: icoChk != -1 exclusiveGroup: icoChk == -1 ? null : (grpID == 1) ? stanceGrp : noGrp } } } } } ================================================ FILE: assets/qml/ActionsGrid.qml ================================================ // Copyright 2016-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 Grid { id: root /* * List of actions. Fields: * 'act' - button action of type 'Action' that may have 'url iconCheckedSource' property * * Use 'act.iconSource == ""' for gaps. */ property var buttonActions spacing: metricsUnit * 0.5 Component { id: gridElement ButtonExtruded { property bool flat width: (root.height - root.spacing * (root.rows - 1)) / root.rows height: width visible: iconSource != "" property url iconCheckedSource } } Repeater { model: parent.buttonActions delegate: Loader { sourceComponent: gridElement active: typeof act !== "undefined" onLoaded: { item.iconSource = Qt.binding(function() { return act.iconSource }) item.action = act item.flat = Qt.binding(function() { return act.iconSourceChecked != "" }) item.iconCheckedSource = Qt.binding(function() { return act.iconCheckedSource }) } } } /* * Metric propagation. */ FontMetrics { id: fontMetrics } property int metricsUnit: metrics ? metrics.unit : fontMetrics.averageCharacterWidth } ================================================ FILE: assets/qml/BindsHelp.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import yay.sfttech.openage 1.0 as OA Column { property var gameControl Text { id: dummyText font.pointSize: 9 } Text { font.pointSize: dummyText.font.pointSize + 3 color: "white" text: gameControl.mode ? gameControl.mode.name : "No Mode" } Repeater { model: gameControlObj.mode ? gameControlObj.mode.binds : undefined Text { font.pointSize: dummyText.font.pointSize color: "white" text: modelData } } Text { font.pointSize: dummyText.font.pointSize + 3 color: "white" text: "Global Bindings" } Repeater { model: OA.Engine.globalBinds Text { font.pointSize: dummyText.font.pointSize color: "white" text: modelData } } } ================================================ FILE: assets/qml/ButtonExtruded.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 Button { style: ButtonExtrudedStyle {} } ================================================ FILE: assets/qml/ButtonExtrudedStyle.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 /* * Additionally accepts 'bool flat' and 'url iconCheckedSource'. */ ButtonStyle { FontMetrics { id: fontMetrics } property int metricsUnit: metrics ? metrics.unit : fontMetrics.averageCharacterWidth background: Rectangle { anchors.fill: parent implicitWidth: 100 implicitHeight: 25 color: control.flat ? "transparent" : control.pressed ? "black" : "white" Image { anchors.fill: parent property int extrusion: control.flat ? 0 : metricsUnit / 4 anchors.leftMargin: control.pressed ? 0 : extrusion anchors.topMargin: anchors.leftMargin anchors.rightMargin: control.pressed ? extrusion : 0 anchors.bottomMargin: anchors.rightMargin source: control.checked ? control.iconCheckedSource : control.iconSource fillMode: Image.Stretch } } label: Item { } } ================================================ FILE: assets/qml/ButtonFlat.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 Button { style: ButtonFlatStyle {} } ================================================ FILE: assets/qml/ButtonFlatStyle.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 ButtonStyle { FontMetrics { id: fontMetrics } QtObject { id: d readonly property color color: control.hovered || control.checked ? "yellow" : "white" } background: Rectangle { implicitWidth: control.iconSource ? 0 : 100 implicitHeight: control.iconSource ? 0 : 25 color: "transparent" border.width: control.text || control.hovered || !control.iconSource || control.checked ? 1 : 0 border.color: control.checked ? "yellow" : "white" radius: 4 } label: Item { implicitWidth: row.implicitWidth + (control.text ? fontMetrics.averageCharacterWidth * 2 : 0) implicitHeight: row.implicitHeight + (control.text ? fontMetrics.averageCharacterWidth : 0) Row { id: row anchors.centerIn: parent Image { sourceSize.width: control.buttonIconWidth sourceSize.height: control.buttonIconHeight source: control.iconSource anchors.verticalCenter: parent.verticalCenter } Text { id: text color: d.color text: control.text } } } } ================================================ FILE: assets/qml/CheckBoxFlat.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 CheckBox { style: CheckBoxFlatStyle {} } ================================================ FILE: assets/qml/CheckBoxFlatStyle.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 CheckBoxStyle { FontMetrics { id: fontMetrics } QtObject { id: metrics readonly property int unit: fontMetrics.averageCharacterWidth } QtObject { id: d readonly property color color: control.hovered ? "yellow" : "white" } indicator: Rectangle { implicitWidth: metrics.unit * 2 implicitHeight: metrics.unit * 2 color: "transparent" border.width: 1 border.color: "white" radius: 4 Rectangle { visible: control.checked color: d.color radius: 1 anchors.margins: metrics.unit * .5 anchors.fill: parent } } label: Text { color: d.color text: control.text } } ================================================ FILE: assets/qml/CreateGameWhenReady.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import yay.sfttech.openage 1.0 as OA OA.GameCreator { id: root property bool enabled: false property var gameControl property int gameControlTargetModeIndex property int specState: gameSpec ? gameSpec.state : OA.GameSpec.Null property int gameState: game ? game.state : OA.GameMain.Null onSpecStateChanged: { if (enabled && specState == OA.GameSpec.Ready) activate() } onGameStateChanged: { if (enabled && gameState == OA.GameMain.Running) if (gameControl.modeIndex != -1) gameControl.modeIndex = gameControlTargetModeIndex else console.error("CreateGameWhenReady: could not find the desired mode to switch to") } } ================================================ FILE: assets/qml/GeneratorControl.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import yay.sfttech.openage 1.0 as OA Item { id: root property var generatorParameters property var gameSpec property var game implicitWidth: elements.width implicitHeight: elements.height Column { id: elements spacing: genActions.spacing ColumnLayout { id: genActions OA.GameSaver { id: gameSaver game: root.game generatorParameters: root.generatorParameters } ButtonFlat { Layout.fillWidth: true text: "save_game" onClicked: gameSaver.activate() } OA.GameCreator { id: gameCreator game: root.game gameSpec: root.gameSpec generatorParameters: root.generatorParameters } ButtonFlat { Layout.fillWidth: true text: "generate_game" onClicked: { gameCreator.activate() gameSaver.clearErrors() } } ButtonFlat { Layout.fillWidth: true text: "end_game" onClicked: { game.clear() gameCreator.clearErrors() } } ButtonFlat { Layout.fillWidth: true text: "reload_assets" onClicked: gameSpec.invalidate() } ButtonFlat { Layout.fillWidth: true text: "quit" onClicked: game.engine.stop() } } Text { property string errorStringSeparator: gameCreator.errorString && gameSaver.errorString ? "\n" : "" property string errorString: gameCreator.errorString + errorStringSeparator + gameSaver.errorString color: errorString ? "red" : "white" text: if (errorString) "Error: " + errorString else switch (root.game.state) { case OA.GameMain.Null: "Not running" break case OA.GameMain.Running: "Running" break default: "Unknown" break } } } } ================================================ FILE: assets/qml/GeneratorParametersConfiguration.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import yay.sfttech.openage 1.0 as OA Item { id: root property var generatorParameters implicitHeight: parameterTable.height GridLayout { id: parameterTable Repeater { model: root.generatorParameters Text { Layout.row: index Layout.column: 0 color: "white" text: display } } Repeater { model: root.generatorParameters Component { id: textField TextFieldFlat { property var model text: model.edit onTextChanged: model.edit = text } } Component { id: checkBox CheckBoxFlat { property var model checked: model.edit onCheckedChanged: model.edit = checked } } delegate: Loader { Layout.row: index Layout.column: 1 sourceComponent: typeof(model.edit) == "boolean" ? checkBox : textField onLoaded: item.model = Qt.binding(function() { return model }) } } } } ================================================ FILE: assets/qml/IngameHud.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import yay.sfttech.openage 1.0 as OA Item { id: root property var actionMode property string playerName property int civIndex readonly property int topStripSubid: 0 readonly property int midStripSubid: 1 readonly property int leftRectSubid: 2 readonly property int rightRectSubid: 3 readonly property int resBaseSubid: 4 readonly property string srcPrefix: "image://by-filename/converted/interface/hud" readonly property string pad: "0000" readonly property string srcSuffix: ".slp.png" property string hudImageSource: srcPrefix + (pad + civIndex).slice(-pad.length) + srcSuffix readonly property string iconsPrefix: "image://by-filename/converted/interface/" readonly property string iconsBorder: "image://by-filename/converted/interface/53003.slp.png.1" width: 1289 height: 960 Item { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top height: metricsUnit * 1.5 * 2.5 Image { anchors.fill: parent source: hudImageSource + "." + root.topStripSubid sourceSize.height: parent.height fillMode: Image.Tile } RowLayout { anchors.fill: parent anchors.leftMargin: metricsUnit anchors.rightMargin: metricsUnit anchors.verticalCenter: parent.verticalCenter spacing: metricsUnit * 0.7 Row { spacing: metricsUnit * 0.7 Component { id: resourceIndicator Rectangle { property string amount property int iconIndex property bool warning width: metricsUnit * 1.5 * 6.5 height: metricsUnit * 1.5 * 1.7 color: "#7FFFFFFF" Rectangle { anchors.fill: parent anchors.rightMargin: metricsUnit * 0.3 anchors.bottomMargin: metricsUnit * 0.3 color: "black" Image { sourceSize.height: parent.height source: hudImageSource + "." + (root.resBaseSubid + iconIndex) fillMode: Image.PreserveAspectFit } Text { anchors.right: parent.right anchors.rightMargin: metricsUnit / 2 anchors.verticalCenter: parent.verticalCenter text: amount color: "white" } } Rectangle { anchors.fill: parent anchors.rightMargin: metricsUnit * 0.3 anchors.bottomMargin: metricsUnit * 0.3 visible: warning color: "#80FFC100" SequentialAnimation on opacity { loops: Animation.Infinite PropertyAnimation { from: 0; to: 1; duration: 250 } PropertyAnimation { from: 1; to: 0; duration: 250 } } } } } Repeater { model: OA.Resources { actionMode: root.actionMode } delegate: Loader { sourceComponent: resourceIndicator onLoaded: { item.amount = Qt.binding(function() { return display }) item.iconIndex = Qt.binding(function() { return index }) } } } Loader { sourceComponent: resourceIndicator onLoaded: { item.amount = Qt.binding(function() { return root.actionMode.population }) item.iconIndex = 4 item.warning = Qt.binding(function() { return root.actionMode.population_warn }) } } } Item { Layout.fillWidth: true Layout.minimumWidth: epoch.implicitWidth Rectangle { anchors.centerIn: parent width: 200 height: metricsUnit * 2.5 color: "black" Text { id: epoch anchors.centerIn: parent color: "white" text: root.playerName } } } Repeater { model: 5 Rectangle { width: metricsUnit * 1.5 * 5 height: metricsUnit * 1.5 * 1.5 color: "transparent" border.width: 1 border.color: "white" } } } } Item { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom height: metricsUnit * 1.5 * 16 Image { id: leftRect anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom width: height * 1.63 source: hudImageSource + "." + root.leftRectSubid fillMode: Image.Stretch ActionsGrid { id: actionsGrid anchors.fill: parent anchors.topMargin: parent.height * 40 / 218 anchors.leftMargin: parent.height * 40 / 218 + metricsUnit * 1.4 anchors.rightMargin: parent.height * 40 / 218 + metricsUnit * 0.5 anchors.bottomMargin: parent.height * 40 / 218 - metricsUnit * 1.4 columns: 5 rows: 3 Actions { id: actions actionMode: root.actionMode } buttonActions: actions.actionObjects } } ColumnLayout { anchors.left: leftRect.right anchors.right: rightRect.left anchors.top: parent.top anchors.bottom: parent.bottom spacing: 0 Image { id: borderStrip Layout.fillWidth: true sourceSize.height: metricsUnit * 1.4 source: hudImageSource + "." + root.midStripSubid fillMode: Image.Tile } Rectangle { Layout.fillHeight: true Layout.fillWidth: true color: "#41110d" Paper { anchors.fill: parent tornBottom: false } Item { anchors.fill: parent id: selection_single_panel visible: root.actionMode.selection_size == 1 Image { anchors.top: parent.top anchors.topMargin: metricsUnit * 2 anchors.left: parent.left anchors.leftMargin: metricsUnit * 2 width: 50 height: 50 id: selected_icon source: root.actionMode.selection_icon ? iconsPrefix + root.actionMode.selection_icon : iconsBorder } Image { anchors.centerIn: selected_icon width: 50 height: 50 source: iconsBorder } Text { anchors.top: selected_icon.top anchors.left: selected_icon.right anchors.leftMargin: metricsUnit * 1.2 id: selected_name color: "black" text: root.actionMode.selection_name font.pointSize: 16 } Text { anchors.verticalCenter: selected_name.verticalCenter anchors.left: selected_name.right anchors.leftMargin: metricsUnit * 1.2 id: selected_type color: "black" opacity: 0.8 text: root.actionMode.selection_type } Text { anchors.top: selected_name.bottom anchors.left: selected_name.left anchors.topMargin: metricsUnit id: selected_hp color: "black" text: root.actionMode.selection_hp } Text { anchors.top: selected_hp.bottom anchors.left: selected_icon.left anchors.topMargin: metricsUnit * 2 color: "black" text: root.actionMode.selection_attrs } Text { anchors.top: parent.top anchors.right: parent.right anchors.topMargin: metricsUnit * 2 anchors.rightMargin: metricsUnit * 2 color: "black" text: root.actionMode.selection_owner horizontalAlignment: Text.AlignRight } } Item { anchors.fill: parent id: selection_group_panel visible: root.actionMode.selection_size > 1 Text { anchors.top: parent.top anchors.topMargin: metricsUnit * 2 anchors.left: parent.left anchors.leftMargin: metricsUnit * 2 color: "black" text: root.actionMode.selection_name font.pointSize: 14 } } Item { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.bottomMargin: metricsUnit * 3 visible: actionMode.ability.length Text { anchors.centerIn: parent text: actionMode.ability font.pointSize: 14 } } } } Image { id: rightRect anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom width: height * 2.01 source: hudImageSource + "." + root.rightRectSubid fillMode: Image.Stretch } } /* * Metric propagation. */ FontMetrics { id: fontMetrics } property int metricsUnit: metrics ? metrics.unit : fontMetrics.averageCharacterWidth } ================================================ FILE: assets/qml/Paper.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 Item { id: root /* * A paper texture. */ property url paperTextureSource: "image://by-filename/converted/interface/scr5b.slp.png.0" /* * This texture is tiled along the item edges, bottom faces the inside. * So, alpha should be zero for the pixels where to keep the paper, solid color - where it's torn off. */ property url edgeHmapSource: "image://by-filename/textures/torn_paper_edge.png.0" /* * Which edges are torn off. */ property bool tornTop: true property bool tornBottom: true property bool tornLeft: true property bool tornRight: true /* * Changing metrics.unit (currently bound to the font) scales all textures. * So the details on the textures stay exactly the same relatively to metricsUnit. */ property real scaling: 8. / metricsUnit transform: Scale { xScale: 1. / root.scaling; yScale: 1. / root.scaling} Item { anchors.left: parent.left anchors.top: parent.top /* * Do +.5 to avoid being less than parent due to truncation. */ width: (parent.width + .5) * root.scaling height: (parent.height + .5) * root.scaling Item { anchors.fill: parent /* * Hack, so the child has 'visible' and 'layer.enabled'. */ clip: true Item { id: texturedPaper /* * Hack, so that this item has 'visible' and 'layer.enabled'. */ width: parent.width height: parent.height x: parent.width layer.enabled: true Image { anchors.fill: parent source: root.paperTextureSource fillMode: Image.Tile /* * Use an ellipse to darken parts that are far from the center. */ layer.enabled: true layer.wrapMode: ShaderEffectSource.Repeat layer.samplerName: "paperSource" layer.effect: ShaderEffect { fragmentShader: " uniform lowp sampler2D paperSource; varying highp vec2 qt_TexCoord0; const float exposure = .5; const float times = 3.; vec3 burntMidtones(vec3 src, float exposure, float times) { float factor = 1. + exposure * (.33); return pow(src.rgb, vec3(factor * times)); } void main() { vec4 bkg = texture2D(paperSource, qt_TexCoord0); vec3 burnt = burntMidtones(bkg.rgb, exposure, times); float burnGradient = pow(pow((qt_TexCoord0.x - .5), 2.) + pow((qt_TexCoord0.y - 1.), 2.), 1.) * .9; gl_FragColor = vec4(mix(bkg.rgb, burnt, vec3(burnGradient)), 1.); } " } } } } /* * Inverse QtGraphicalEffects.OpacityMask. */ Item { anchors.fill: parent layer.enabled: true layer.effect: ShaderEffect { property variant source: texturedPaper property variant mask: rectWithTornOffEdges fragmentShader: " uniform sampler2D source; uniform sampler2D mask; varying highp vec2 qt_TexCoord0; void main() { gl_FragColor = texture2D(source, qt_TexCoord0) * (1. - texture2D(mask, qt_TexCoord0).a); } " } } /* * Alpha == 1.0 where the edges are torn off. */ Item { id: rectWithTornOffEdges anchors.fill: parent layer.enabled: true Image { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top source: root.edgeHmapSource fillMode: Image.Tile visible: root.tornTop } Image { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top source: root.edgeHmapSource fillMode: Image.Tile transform: Rotation { origin.x: rectWithTornOffEdges.width / 2 origin.y: rectWithTornOffEdges.height / 2 angle: 180 } visible: root.tornBottom } Image { anchors.left: parent.left anchors.leftMargin: parent.height anchors.top: parent.top width: parent.height source: root.edgeHmapSource fillMode: Image.Tile transform: Rotation { origin.y: rectWithTornOffEdges.height angle: -90 } visible: root.tornLeft } Image { anchors.right: parent.right anchors.rightMargin: parent.height anchors.top: parent.top width: parent.height source: root.edgeHmapSource fillMode: Image.Tile transform: Rotation { origin.y: rectWithTornOffEdges.height origin.x: rectWithTornOffEdges.height angle: 90 } visible: root.tornRight } visible: false } } /* * Average character width that the paper will adapt to (by scaling its texture). */ /* * Metric propagation. */ FontMetrics { id: fontMetrics } property int metricsUnit: metrics ? metrics.unit : fontMetrics.averageCharacterWidth } ================================================ FILE: assets/qml/TextFieldFlat.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 TextField { style: TextFieldFlatStyle {} } ================================================ FILE: assets/qml/TextFieldFlatStyle.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.3 TextFieldStyle { textColor: "white" background: Rectangle { color: "transparent" border.width: 1 border.color: "white" radius: 4 } } ================================================ FILE: assets/qml/TypePicker.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import yay.sfttech.openage 1.0 as OA Item { id: root // input property var editorMode property var gameSpec property int iconHeight function toggle() { if (!categoryPicker.current && categoryButtons.children.length) categoryButtons.children[0].checked = true else for (var i = 0; i < categoryButtons.children.length; ++i) { var item = categoryButtons.children[i] if (item.exclusiveGroup && item.exclusiveGroup == categoryPicker && item.checked) { for (var j = (i + 1) % categoryButtons.children.length; j != i; j = (j + 1) % categoryButtons.children.length) { var candidate = categoryButtons.children[j] if (candidate.exclusiveGroup && candidate.exclusiveGroup == categoryPicker) { candidate.checked = true break } } break } } } // output readonly property int current: selectedType.current ? selectedType.current.thisTypeId : -1 property int currentHighlighted: -1 readonly property int currentTerrain: selectedTerrain.current ? selectedTerrain.current.thisTerrainId : -1 readonly property bool paintTerrain: categoryPicker.current && categoryPicker.current.text == "terrain" ColumnLayout { anchors.fill: parent Row { id: categoryButtons anchors.horizontalCenter: implicitWidth < parent.width ? parent.horizontalCenter : undefined ExclusiveGroup { id: categoryPicker } OA.Category { id: categoryToUse name: categoryPicker.current && categoryPicker.current.text != "terrain" ? categoryPicker.current.text : "" editorMode: root.editorMode } ButtonFlat { checkable: true exclusiveGroup: categoryPicker text: "terrain" } Repeater { model: editorMode.categories ButtonFlat { checkable: true exclusiveGroup: categoryPicker text: modelData } } } Rectangle { Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: metricsUnit * 10 Layout.minimumHeight: metricsUnit * 10 color: "transparent" border.width: 1 border.color: "white" radius: 4 Flickable { anchors.fill: parent anchors.margins: metricsUnit / 2 boundsBehavior: Flickable.StopAtBounds clip: true contentWidth: width contentHeight: typeIcons.height Flow { id: typeIcons anchors.left: parent.left anchors.right: parent.right ExclusiveGroup { id: selectedType } Repeater { model: categoryPicker.current && categoryPicker.current.text != "terrain" ? categoryToUse : undefined ButtonFlat { checkable: true exclusiveGroup: selectedType property int thisTypeId: typeId property int buttonIconHeight: iconHeight iconSource: "image://by-graphic-id/" + display + ".0" onHoveredChanged: { if (hovered) root.currentHighlighted = typeId else if (root.currentHighlighted == typeId) root.currentHighlighted = -1 } } } ExclusiveGroup { id: selectedTerrain } Repeater { model: categoryPicker.current && categoryPicker.current.text == "terrain" ? gameSpec.terrainIdCount : undefined ButtonFlat { checkable: true exclusiveGroup: selectedTerrain property int thisTerrainId: index property int buttonIconHeight: iconHeight iconSource: "image://by-terrain-id/" + index + ".0" } } } } } } /* * Metric propagation. */ FontMetrics { id: fontMetrics } property int metricsUnit: metrics ? metrics.unit : fontMetrics.averageCharacterWidth } ================================================ FILE: assets/qml/main.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 import QtQuick.Controls.Styles 1.3 import yay.sfttech.livereload 1.0 import yay.sfttech.openage 1.0 as OA Item { id: root /* * Global metric declaration. */ FontMetrics { id: fontMetrics } QtObject { id: metrics property real scale: 1 property int unit: fontMetrics.averageCharacterWidth * scale } OA.GameSpec { id: specObj /** * States: Null, Loading, Ready */ // Start loading assets immediately active: true assetManager: amObj LR.tag: "spec" } OA.LegacyAssetManager { id: amObj assetDir: OA.MainArgs.assetDir engine: OA.Engine LR.tag: "am" } OA.GameMain { id: gameObj /** * States: Null, Running */ engine: OA.Engine LR.tag: "game" } OA.GameControl { id: gameControlObj engine: OA.Engine game: gameObj /** * must be run after the engine is attached */ Component.onCompleted: { modes = [createModeObj, editorModeObj, actionModeObj] modeIndex = 0 } LR.tag: "gamecontrol" } OA.GeneratorParameters { id: genParamsObj LR.tag: "gen" } ColumnLayout { id: controls anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top spacing: fontMetrics.averageCharacterWidth * 2 state: gameControlObj.mode ? gameControlObj.mode.name : "" Component { id: changeMode ButtonFlat { text: "change_mode" onClicked: gameControlObj.modeIndex = (gameControlObj.effectiveModeIndex + 1) % gameControlObj.modes.length } } OA.CreateMode { id: createModeObj LR.tag: "createMode" } OA.ActionMode { id: actionModeObj LR.tag: "actionMode" } OA.EditorMode { id: editorModeObj currentTypeId: typePicker.current currentTerrainId: typePicker.currentTerrain paintTerrain: typePicker.paintTerrain onToggle: typePicker.toggle() LR.tag: "editorMode" } CreateGameWhenReady { enabled: createWhenReady.checked game: gameObj gameSpec: specObj generatorParameters: genParamsObj gameControl: gameControlObj gameControlTargetModeIndex: gameControlObj.modes.indexOf(actionModeObj) } states: [ State { id: creationMode name: createModeObj.name property list content: [ Loader { sourceComponent: changeMode }, GeneratorParametersConfiguration { generatorParameters: genParamsObj }, GeneratorControl { generatorParameters: genParamsObj gameSpec: specObj game: gameObj }, CheckBoxFlat { id: createWhenReady text: "create_when_ready" visible: specObj.state == OA.GameSpec.Loading } ] PropertyChanges { target: controls children: creationMode.content anchors.topMargin: fontMetrics.averageCharacterWidth * 4 anchors.leftMargin: fontMetrics.averageCharacterWidth * 2 } }, State { id: editorMode name: editorModeObj.name property list content: [ Loader { sourceComponent: changeMode }, TypePicker { id: typePicker Layout.preferredWidth: root.width / 4 Layout.preferredHeight: root.height / 2 iconHeight: fontMetrics.averageCharacterWidth * 8 editorMode: editorModeObj gameSpec: specObj }, Text { color: "white" text: typePicker.currentHighlighted != -1 ? typePicker.currentHighlighted : "" } ] PropertyChanges { target: controls children: editorMode.content anchors.topMargin: fontMetrics.averageCharacterWidth * 4 anchors.leftMargin: fontMetrics.averageCharacterWidth * 2 } }, State { id: actionMode name: actionModeObj.name property list content: [ IngameHud { anchors.fill: root actionMode: actionModeObj playerName: gameControlObj.currentPlayerName civIndex: gameControlObj.currentCivIndex } ] PropertyChanges { target: controls; children: actionMode.content } } ] } ColumnLayout { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.topMargin: fontMetrics.averageCharacterWidth * 4 anchors.rightMargin: fontMetrics.averageCharacterWidth * 2 spacing: fontMetrics.averageCharacterWidth * 2 BindsHelp { Layout.alignment: Qt.AlignRight gameControl: gameControlObj } } Component.onCompleted: { OA.ImageProviderByFilename.gameSpec = specObj OA.ImageProviderById.gameSpec = specObj OA.ImageProviderByTerrainId.gameSpec = specObj } } ================================================ FILE: assets/shaders/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/shaders" FILES_MATCHING PATTERN "*.glsl" ) ================================================ FILE: assets/shaders/alphamask.frag.glsl ================================================ // alpha masking shader // // applies an alpha mask texture to a base texture, // then draws the masked texture. // the base and mask texture, base is the plain terrain tile uniform sampler2D base_texture; uniform sampler2D mask_texture; // disable blending and show the mask instead uniform bool show_mask; // get those interpolated texture position from vertexshader varying vec2 base_tex_position; varying vec2 mask_tex_position; void main() { // get the texel from the uniform texture. vec4 base_pixel = texture2D(base_texture, base_tex_position); vec4 mask_pixel = texture2D(mask_texture, mask_tex_position); float factor = 1.0 - mask_pixel.x; vec4 blended_pixel = vec4(base_pixel.r, base_pixel.g, base_pixel.b, base_pixel.a - factor); if (show_mask) { gl_FragColor = mask_pixel; } else { gl_FragColor = blended_pixel; } } ================================================ FILE: assets/shaders/alphamask.vert.glsl ================================================ // vertex shader for applying an alpha mask to a texture // modelview*projection matrix uniform mat4 mvp_matrix; // the position of this vertex attribute vec4 vertex_position; // send the texture coordinates to the fragmentshader attribute vec2 base_tex_coordinates; attribute vec2 mask_tex_coordinates; // send the texture coordinates to the fragmentshader varying vec2 base_tex_position; varying vec2 mask_tex_position; void main(void) { // transform the position with the mvp matrix gl_Position = gl_ModelViewProjectionMatrix * vertex_position; // set the fixpoints for the tex coordinates at this vertex mask_tex_position = mask_tex_coordinates; base_tex_position = base_tex_coordinates; } ================================================ FILE: assets/shaders/equalsEpsilon.glsl ================================================ bool equalsEpsilon(vec4 left, vec4 right, float epsilon) { return all(lessThanEqual(abs(left - right), vec4(epsilon))); } bool equalsEpsilon(vec3 left, vec3 right, float epsilon) { return all(lessThanEqual(abs(left - right), vec3(epsilon))); } bool equalsEpsilon(vec2 left, vec2 right, float epsilon) { return all(lessThanEqual(abs(left - right), vec2(epsilon))); } bool equalsEpsilon(float left, float right, float epsilon) { return (abs(left - right) <= epsilon); } ================================================ FILE: assets/shaders/final.frag.glsl ================================================ #version 330 in vec2 tex_pos; out vec4 out_col; uniform sampler2D tex; void main() { out_col = texture(tex, tex_pos); } ================================================ FILE: assets/shaders/final.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 tex_pos; void main() { gl_Position = vec4(position, 0.0, 1.0); tex_pos = uv; } ================================================ FILE: assets/shaders/hud_drag_select.frag.glsl ================================================ #version 330 // Color of the drag rectangle uniform vec4 in_col; layout(location=0) out vec4 out_col; void main() { out_col = in_col; } ================================================ FILE: assets/shaders/hud_drag_select.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } ================================================ FILE: assets/shaders/identity.vert.glsl ================================================ #version 120 // no-transformation texture mapping vertex shader // the position of this vertex attribute vec4 vertex_position; // interpolated texture coordinates sent to fragment shader varying vec2 tex_position; void main(void) { gl_Position = vertex_position; // convert from screen coordinates (-1, 1) to texture coordinates (0, 1) tex_position = (vertex_position.xy + vec2(1.f)) / 2.f; } ================================================ FILE: assets/shaders/maptexture.frag.glsl ================================================ #version 120 // total basic standard texture drawing fragment shader // the texture data uniform sampler2D texture; // interpolated texture coordinates received from vertex shader varying vec2 tex_position; void main (void) { // this sets the fragment color to the corresponding texel. gl_FragColor = texture2D(texture, tex_position); } ================================================ FILE: assets/shaders/maptexture.vert.glsl ================================================ //total basic standard texture mapping vertex shader //modelview*projection matrix uniform mat4 mvp_matrix; //the position of this vertex attribute vec4 vertex_position; //the texture coordinates assigned to this vertex attribute vec2 tex_coordinates; //interpolated texture coordinates sent to fragment shader varying vec2 tex_position; void main(void) { //transform the vertex coordinates gl_Position = gl_ModelViewProjectionMatrix * vertex_position; //pass the fix points for texture coordinates set at this vertex tex_position = tex_coordinates; } ================================================ FILE: assets/shaders/skybox.frag.glsl ================================================ #version 330 uniform vec4 in_col; layout(location=0) out vec4 out_col; void main() { out_col = in_col; } ================================================ FILE: assets/shaders/skybox.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } ================================================ FILE: assets/shaders/teamcolors.frag.glsl ================================================ //team color replacement shader // //looks for an alpha value specified by 'alpha_marker' //and then replaces this pixel with the desired color, //tinted for player_number, and using the given color as base. //the unmodified texture itself uniform sampler2D texture; //the desired player number the final resulting colors uniform int player_number; //the alpha value which marks colors to be replaced uniform float alpha_marker; //color entries for all players and their subcolors uniform vec4 player_color[NUM_OF_PLAYER_COLORS]; //interpolated texture coordinates sent from vertex shader varying vec2 tex_position; //create epsilon environment for float comparison const float EPSILON = 0.001; //do the lookup in the player color table //for a playernumber (red, blue, etc) //get the subcolor (brightness variations) vec4 get_color(int playernum, int subcolor) { return player_color[((playernum-1) * 8) + subcolor]; } void main() { //get the texel from the uniform texture. vec4 pixel = texture2D(texture, tex_position); //check if this texel has an alpha marker, so we can replace it's rgb values. if (player_number != 1 && equalsEpsilon(pixel[3], alpha_marker, EPSILON)) { //try to find the base color, there are 8 of them. for(int i = 0; i <= 7; i++) { if (equalsEpsilon(vec3(pixel), vec3(player_color[i]), EPSILON)) { //base color found, now replace it with the same color //but player_number tinted. gl_FragColor = get_color(player_number, i); return; } } //unknown base color gets pink muhahaha pixel = vec4(255.0/255.0, 20.0/255.0, 147.0/255.0, 1.0); } //else the texel had no marker so we can just draw it without player coloring gl_FragColor = pixel; } ================================================ FILE: assets/shaders/terrain.frag.glsl ================================================ #version 330 in vec2 tex_pos; layout(location=0) out vec4 out_col; uniform sampler2D tex; void main() { vec4 tex_val = texture(tex, tex_pos); out_col = tex_val; } ================================================ FILE: assets/shaders/terrain.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; layout (location = 1) in vec2 uv; out vec2 tex_pos; uniform mat4 model; // camera parameters for transforming the object position // and scaling the subtex to the correct size layout (std140) uniform camera { // view matrix (world to view space) mat4 view; // projection matrix (view to clip space) mat4 proj; // inverse zoom factor (1.0 / zoom) float inv_zoom; // inverse viewport size (1.0 / viewport size) vec2 inv_viewport_size; }; void main() { gl_Position = proj * view * model * vec4(position, 1.0); tex_pos = vec2(uv.x, 1.0 - uv.y); } ================================================ FILE: assets/shaders/texturefont.frag.glsl ================================================ varying vec2 tex_position; uniform sampler2D texture; uniform vec4 color; void main() { // Glyph's image data is stored in the RED channel of the texture float red = texture2D(texture, tex_position).r; if (red < 0.01) { discard; } gl_FragColor = color * vec4(1.0f, 1.0f, 1.0f, red); } ================================================ FILE: assets/shaders/texturefont.vert.glsl ================================================ attribute vec4 vertex_position; attribute vec2 tex_coordinates; varying vec2 tex_position; void main() { gl_Position = gl_ModelViewProjectionMatrix * vertex_position; tex_position = tex_coordinates; } ================================================ FILE: assets/shaders/world2d.frag.glsl ================================================ #version 330 in vec2 vert_uv; layout(location=0) out vec4 col; layout(location=1) out uint id; uniform sampler2D tex; uniform uint u_id; // position (top left corner) and size: (x, y, width, height) uniform vec4 tile_params; vec2 uv = vec2( vert_uv.x * tile_params.z + tile_params.x, vert_uv.y * tile_params.w + tile_params.y ); void main() { vec4 tex_val = texture(tex, uv); int alpha = int(round(tex_val.a * 255)); switch (alpha) { case 0: col = tex_val; discard; // do not save the ID return; case 254: col = vec4(1.0f, 0.0f, 0.0f, 1.0f); break; case 252: col = vec4(0.0f, 1.0f, 0.0f, 1.0f); break; case 250: col = vec4(0.0f, 0.0f, 1.0f, 1.0f); break; default: col = tex_val; break; } id = u_id; } ================================================ FILE: assets/shaders/world2d.vert.glsl ================================================ #version 330 layout(location=0) in vec2 v_position; layout(location=1) in vec2 uv; out vec2 vert_uv; // camera parameters for transforming the object position // and scaling the subtex to the correct size layout (std140) uniform camera { // view matrix (world to view space) mat4 view; // projection matrix (view to clip space) mat4 proj; // inverse zoom factor (1.0 / zoom) // high zoom = upscale subtex // low zoom = downscale subtex float inv_zoom; // inverse viewport size (1.0 / viewport size) vec2 inv_viewport_size; }; // can be used to move the object position in world space _before_ // it's transformed to clip space // this is usually unnecessary because we want to draw the // subtex where the object is, so this can be set to the identity matrix uniform mat4 model; // position of the object in world space uniform vec3 obj_world_position; // flip the subtexture horizontally/vertically uniform bool flip_x; uniform bool flip_y; // parameters for scaling and moving the subtex // to the correct position in clip space // animation scalefactor // scales the vertex positions so that they // match the subtex dimensions // // high animation scale = downscale subtex // low animation scale = upscale subtex uniform float scale; // size of the subtex (in pixels) uniform vec2 subtex_size; // offset of the subtex anchor point // from the subtex center (in pixels) // used to move the subtex so that the anchor point // is at the object position uniform vec2 anchor_offset; void main() { // translate the position of the object from world space to clip space // this is the position where we want to draw the subtex in 2D vec4 obj_clip_pos = proj * view * model * vec4(obj_world_position, 1.0); // subtex has to be scaled to account for the zoom factor // and the animation scale factor. essentially this is (animation scale / zoom). float zoom_scale = scale * inv_zoom; // Scale the subtex vertices // we have to account for the viewport size to get the correct dimensions // and then scale the subtex to the zoom factor to get the correct size vec2 vert_scale = zoom_scale * subtex_size * inv_viewport_size; // Scale the anchor offset with the same method as above // to get the correct anchor position in the viewport vec2 anchor_scale = zoom_scale * anchor_offset * inv_viewport_size; // if the subtex is flipped, we also need to flip the anchor offset // essentially, we invert the coordinates for the flipped axis float anchor_x = float(flip_x) * -1.0 * anchor_scale.x + float(!flip_x) * anchor_scale.x; float anchor_y = float(flip_y) * -1.0 * anchor_scale.y + float(!flip_y) * anchor_scale.y; // offset the clip position by the offset of the subtex anchor // imagine this as pinning the subtex to the object position at the subtex anchor point obj_clip_pos += vec4(anchor_x, anchor_y, 0.0, 0.0); // create a move matrix for positioning the vertices // uses the vert scale and the transformed object position in clip space mat4 move = mat4(vert_scale.x, 0.0, 0.0, 0.0, 0.0, vert_scale.y, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, obj_clip_pos.x, obj_clip_pos.y, obj_clip_pos.z, 1.0); // calculate the final vertex position gl_Position = move * vec4(v_position, 0.0, 1.0); // if the subtex is flipped, we also need to flip the uv tex coordinates // essentially, we invert the coordinates for the flipped axis // !flip_x is default because OpenGL uses bottom-left as its origin float uv_x = float(!flip_x) * uv.x + float(flip_x) * (1.0 - uv.x); float uv_y = float(flip_y) * uv.y + float(!flip_y) * (1.0 - uv.y); vert_uv = vec2(uv_x, uv_y); } ================================================ FILE: assets/shaders/world3d.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 vert_uv; uniform mat4 model; uniform mat4 view; uniform mat4 proj; void main() { gl_Position = proj * view * model * vec4(position, 0.0, 1.0); vert_uv = vec2(uv.x, 1.0 - uv.y); } ================================================ FILE: assets/test/CMakeLists.txt ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. add_subdirectory(nyan/) add_subdirectory(qml/) add_subdirectory(shaders/) add_subdirectory(textures/) ================================================ FILE: assets/test/nyan/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/test/nyan" FILES_MATCHING PATTERN "*.nyan" ) ================================================ FILE: assets/test/nyan/pong.nyan ================================================ !version 1 PongGame(): ball : Ball player1 : Player player2 : Player Ball(): Color(): r : int = 0 g : int = 200 b : int = 0 color : Color = Color Player(): lives : int = 3 size : int = 200 GameTest(PongGame): ball = Ball player1 = Player player2 = Player LeftColor(): r = 0 g = 20 b = 230 RightColor(): r = 180 g = 40 b = 0 ================================================ FILE: assets/test/qml/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/test/qml" FILES_MATCHING PATTERN "*.qml" ) ================================================ FILE: assets/test/qml/main.qml ================================================ // Copyright 2015-2017 the openage authors. See copying.md for legal info. import QtQuick 2.4 import yay.sfttech.livereload 1.0 import yay.sfttech.openage 1.0 as OA Item { id: root Rectangle { width: 200 height: 200 color: "red" Rectangle { x: 100 y: 100 width: 100 height: 100 color: "blue" Rectangle { id: r width: 50 height: 50 color: "green" } } MouseArea { anchors.fill: parent onClicked: r.color = "yellow" } } } ================================================ FILE: assets/test/shaders/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/test/shaders" FILES_MATCHING PATTERN "*.glsl" ) ================================================ FILE: assets/test/shaders/demo_0_display.frag.glsl ================================================ #version 330 out vec4 col; void main() { col = vec4(1.0, 0.4, 0.0, 0.8); } ================================================ FILE: assets/test/shaders/demo_0_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/demo_1_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/demo_1_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/demo_1_obj.frag.glsl ================================================ #version 330 in vec2 v_uv; uniform sampler2D tex; uniform uint u_id; layout(location=0) out vec4 col; layout(location=1) out uint id; void main() { vec4 tex_val = texture(tex, v_uv); if (tex_val.a == 0) { discard; } col = tex_val; id = u_id; } ================================================ FILE: assets/test/shaders/demo_1_obj.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; uniform mat4 mv; uniform mat4 proj; out vec2 v_uv; void main() { gl_Position = proj * mv * vec4(position, 0.0, 1.0); v_uv = vec2(uv.x, 1.0 - uv.y); } ================================================ FILE: assets/test/shaders/demo_2_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/demo_2_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/demo_2_obj.frag.glsl ================================================ #version 330 in vec2 v_uv; uniform sampler2D tex; uniform uint u_id; layout(location=0) out vec4 col; layout(location=1) out uint id; void main() { vec4 tex_val = texture(tex, v_uv); int alpha = int(round(tex_val.a * 255)); switch (alpha) { case 0: discard; break; case 254: col = vec4(1.0f, 0.0f, 0.0f, 1.0f); break; case 252: col = vec4(0.0f, 1.0f, 0.0f, 1.0f); break; case 250: col = vec4(0.0f, 0.0f, 1.0f, 1.0f); break; default: col = tex_val; break; } id = u_id; } ================================================ FILE: assets/test/shaders/demo_2_obj.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; uniform mat4 mv; uniform mat4 proj; uniform vec4 offset_tile; float width = offset_tile.y - offset_tile.x; float height = offset_tile.w - offset_tile.z; out vec2 v_uv; void main() { gl_Position = proj * mv * vec4(position, 0.0, 1.0); v_uv = vec2((uv.x * width) + offset_tile.x, (((1.0 - uv.y) * height) + offset_tile.z)); } ================================================ FILE: assets/test/shaders/demo_4_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/demo_4_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/demo_4_obj.frag.glsl ================================================ #version 330 in vec2 v_uv; uniform sampler2D tex; uniform uint u_id; layout(location=0) out vec4 col; layout(location=1) out uint id; void main() { vec4 tex_val = texture(tex, v_uv); int alpha = int(round(tex_val.a * 255)); switch (alpha) { case 0: discard; break; case 254: col = vec4(1.0f, 0.0f, 0.0f, 1.0f); break; case 252: col = vec4(0.0f, 1.0f, 0.0f, 1.0f); break; case 250: col = vec4(0.0f, 0.0f, 1.0f, 1.0f); break; default: col = tex_val; break; } id = u_id; } ================================================ FILE: assets/test/shaders/demo_4_obj.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; uniform mat4 mv; uniform mat4 proj; uniform vec4 offset_tile; float width = offset_tile.y - offset_tile.x; float height = offset_tile.w - offset_tile.z; out vec2 v_uv; void main() { gl_Position = proj * mv * vec4(position, 0.0, 1.0); v_uv = vec2((uv.x * width) + offset_tile.x, (((1.0 - uv.y) * height) + offset_tile.z)); } ================================================ FILE: assets/test/shaders/demo_5_obj.frag.glsl ================================================ #version 330 in vec2 tex_pos; layout(location=0) out vec4 out_col; uniform sampler2D tex; void main() { vec4 tex_val = texture(tex, tex_pos); out_col = tex_val; } ================================================ FILE: assets/test/shaders/demo_5_obj.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; layout (location = 1) in vec2 uv; out vec2 tex_pos; uniform mat4 model; layout (std140) uniform cam { mat4 view; mat4 proj; }; void main() { gl_Position = proj * view * model * vec4(position, 1.0); tex_pos = vec2(uv.x, 1.0 - uv.y); } ================================================ FILE: assets/test/shaders/demo_6_2d.frag.glsl ================================================ #version 330 in vec2 vert_uv; layout(location=0) out vec4 col; uniform sampler2D tex; // position (top left corner) and size: (x, y, width, height) uniform vec4 tile_params; vec2 uv = vec2( vert_uv.x * tile_params.z + tile_params.x, vert_uv.y * tile_params.w + tile_params.y ); void main() { vec4 tex_val = texture(tex, uv); int alpha = int(round(tex_val.a * 255)); switch (alpha) { case 0: col = tex_val; discard; case 254: col = vec4(1.0f, 0.0f, 0.0f, 1.0f); break; case 252: col = vec4(0.0f, 1.0f, 0.0f, 1.0f); break; case 250: col = vec4(0.0f, 0.0f, 1.0f, 1.0f); break; default: col = tex_val; break; } } ================================================ FILE: assets/test/shaders/demo_6_2d.vert.glsl ================================================ #version 330 layout(location=0) in vec2 v_position; layout(location=1) in vec2 uv; out vec2 vert_uv; // camera parameters for transforming the object position // and scaling the subtex to the correct size layout (std140) uniform camera { // view matrix (world to view space) mat4 view; // projection matrix (view to clip space) mat4 proj; // inverse zoom factor (1.0 / zoom) // high zoom = upscale subtex // low zoom = downscale subtex float inv_zoom; // inverse viewport size (1.0 / viewport size) vec2 inv_viewport_size; }; // position of the object in world space uniform vec3 obj_world_position; // parameters for scaling and moving the subtex // to the correct position in clip space // animation scalefactor // scales the vertex positions so that they // match the subtex dimensions // // high animation scale = downscale subtex // low animation scale = upscale subtex uniform float scale; // size of the subtex (in pixels) uniform vec2 subtex_size; // offset of the subtex anchor point // from the subtex center (in pixels) // used to move the subtex so that the anchor point // is at the object position uniform vec2 anchor_offset; void main() { // translate the position of the object from world space to clip space // this is the position where we want to draw the subtex in 2D vec4 obj_clip_pos = proj * view * vec4(obj_world_position, 1.0); // subtex has to be scaled to account for the zoom factor // and the animation scale factor. essentially this is (animation scale / zoom). float zoom_scale = scale * inv_zoom; // Scale the subtex vertices // we have to account for the viewport size to get the correct dimensions // and then scale the subtex to the zoom factor to get the correct size vec2 vert_scale = zoom_scale * subtex_size * inv_viewport_size; // Scale the anchor offset with the same method as above // to get the correct anchor position in the viewport vec2 anchor_scale = zoom_scale * anchor_offset * inv_viewport_size; // offset the clip position by the offset of the subtex anchor // imagine this as pinning the subtex to the object position at the subtex anchor point obj_clip_pos += vec4(anchor_scale.x, anchor_scale.y, 0.0, 0.0); // create a move matrix for positioning the vertices // uses the vert scale and the transformed object position in clip space mat4 move = mat4(vert_scale.x, 0.0, 0.0, 0.0, 0.0, vert_scale.y, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, obj_clip_pos.x, obj_clip_pos.y, obj_clip_pos.z, 1.0); // calculate the final vertex position gl_Position = move * vec4(v_position, 0.0, 1.0); // flip y axis because OpenGL uses bottom-left as its origin float uv_x = uv.x; float uv_y = 1.0 - uv.y; vert_uv = vec2(uv_x, uv_y); } ================================================ FILE: assets/test/shaders/demo_6_2d_frame.frag.glsl ================================================ #version 330 out vec4 outcol; uniform vec4 incol; void main() { outcol = incol; } ================================================ FILE: assets/test/shaders/demo_6_2d_frame.vert.glsl ================================================ #version 330 layout(location=0) in vec2 v_position; // camera parameters for transforming the object position // and scaling the subtex to the correct size layout (std140) uniform camera { // view matrix (world to view space) mat4 view; // projection matrix (view to clip space) mat4 proj; // inverse zoom factor (1.0 / zoom) float inv_zoom; // inverse viewport size (1.0 / viewport size) vec2 inv_viewport_size; }; // position of the object in world space uniform vec3 obj_world_position; // parameters for scaling and moving the subtex // to the correct position in clip space // animation scalefactor // scales the vertex positions so that they // match the subtex dimensions // // high animation scale = downscale subtex // low animation scale = upscale subtex uniform float scale; // size of the frame (in pixels) uniform vec2 frame_size; void main() { // translate the position of the object from world space to clip space // this is the position where we want to draw the subtex in 2D vec4 obj_clip_pos = proj * view * vec4(obj_world_position, 1.0); // subtex has to be scaled to account for the zoom factor // and the animation scale factor. essentially this is (animation scale / zoom). float zoom_scale = scale * inv_zoom; // Scale the subtex vertices // we have to account for the viewport size to get the correct dimensions // and then scale the frame to the zoom factor to get the correct size vec2 vert_scale = zoom_scale * frame_size * inv_viewport_size; // create a move matrix for positioning the vertices // uses the vert scale and the transformed object position in clip space mat4 move = mat4(vert_scale.x, 0.0, 0.0, 0.0, 0.0, vert_scale.y, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, obj_clip_pos.x, obj_clip_pos.y, obj_clip_pos.z, 1.0); // calculate the final vertex position gl_Position = move * vec4(v_position, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/demo_6_2d_frustum_frame.vert.glsl ================================================ #version 330 layout(location=0) in vec2 v_position; void main() { // flip the y coordinate in OpenGL gl_Position = vec4(v_position.x, v_position.y, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/demo_6_3d.frag.glsl ================================================ #version 330 in vec2 tex_pos; layout(location=0) out vec4 out_col; uniform sampler2D tex; void main() { vec4 tex_val = texture(tex, tex_pos); out_col = tex_val; } ================================================ FILE: assets/test/shaders/demo_6_3d.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; layout (location = 1) in vec2 uv; out vec2 tex_pos; // camera parameters for transforming the object position // and scaling the subtex to the correct size layout (std140) uniform camera { // view matrix (world to view space) mat4 view; // projection matrix (view to clip space) mat4 proj; // inverse zoom factor (1.0 / zoom) float inv_zoom; // inverse viewport size (1.0 / viewport size) vec2 inv_viewport_size; }; void main() { gl_Position = proj * view * vec4(position, 1.0); tex_pos = vec2(uv.x, 1.0 - uv.y); } ================================================ FILE: assets/test/shaders/demo_6_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/demo_6_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/demo_7_shader_command.frag.glsl ================================================ #version 330 in vec2 tex_coord; out vec4 frag_color; uniform float time; void main() { // PLACEHOLDER: color // PLACEHOLDER: alpha frag_color = vec4(r, g, b, alpha); } ================================================ FILE: assets/test/shaders/demo_7_shader_command.vert.glsl ================================================ #version 330 in vec2 position; out vec2 tex_coord; void main() { tex_coord = position.xy * 0.5 + 0.5; gl_Position = vec4(position.xy, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/demo_7_snippets/alpha.snippet ================================================ float alpha = (sin(time) + 1.0) * 0.5; ================================================ FILE: assets/test/shaders/demo_7_snippets/color.snippet ================================================ float r = 0.5 + 0.5 * sin(time); float g = 0.5 + 0.5 * sin(time + 2.0); float b = 0.5 + 0.5 * sin(time + 4.0); ================================================ FILE: assets/test/shaders/pathfinding/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/test/shaders/pathfinding" FILES_MATCHING PATTERN "*.glsl" ) ================================================ FILE: assets/test/shaders/pathfinding/demo_0_cost_field.frag.glsl ================================================ #version 330 in float v_cost; out vec4 out_col; void main() { if (v_cost == 255.0) { out_col = vec4(0.0, 0.0, 0.0, 1.0); return; } float cost = (v_cost / 256) * 2.0; float red = clamp(cost, 0.0, 1.0); float green = clamp(2.0 - cost, 0.0, 1.0); out_col = vec4(red, green, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_cost_field.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; layout (location = 1) in float cost; uniform mat4 model; uniform mat4 view; uniform mat4 proj; out float v_cost; void main() { gl_Position = proj * view * model * vec4(position, 1.0); v_cost = cost; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_flow_field.frag.glsl ================================================ #version 330 /// Flow field value in float flow_val; /// Integration field flags in float int_val; out vec4 outcol; int WAVEFRONT_BLOCKED = 0x04; int LINE_OF_SIGHT = 0x20; int PATHABLE = 0x10; void main() { int flow_flags = int(flow_val) & 0xF0; int int_flags = int(int_val); if (bool(int_flags & WAVEFRONT_BLOCKED)) { // wavefront blocked outcol = vec4(0.9, 0.9, 0.9, 1.0); return; } if (bool(int_flags & LINE_OF_SIGHT)) { // line of sight outcol = vec4(1.0, 1.0, 1.0, 1.0); return; } if (bool(flow_flags & PATHABLE)) { // pathable outcol = vec4(0.7, 0.7, 0.7, 1.0); return; } // not pathable outcol = vec4(0.0, 0.0, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_flow_field.vert.glsl ================================================ #version 330 layout(location=0) in vec3 position; layout(location=1) in float flow_cell; layout(location=2) in float int_cell; uniform mat4 model; uniform mat4 view; uniform mat4 proj; out float flow_val; out float int_val; void main() { gl_Position = proj * view * model * vec4(position, 1.0); flow_val = flow_cell; int_val = int_cell; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_grid.frag.glsl ================================================ #version 330 out vec4 out_col; void main() { out_col = vec4(0.0, 0.0, 0.0, 0.3); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_grid.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; uniform mat4 model; uniform mat4 view; uniform mat4 proj; void main() { gl_Position = proj * view * model * vec4(position, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_integration_field.frag.glsl ================================================ #version 330 in float v_cost; out vec4 out_col; void main() { if (v_cost > 512.0) { out_col = vec4(0.0, 0.0, 0.0, 1.0); return; } float cost = 0.05 * v_cost; float green = clamp(1.0 - cost, 0.0, 1.0); out_col = vec4(0.75, green, 0.5, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_integration_field.vert.glsl ================================================ #version 330 layout (location = 0) in vec3 position; layout (location = 1) in float cost; uniform mat4 model; uniform mat4 view; uniform mat4 proj; out float v_cost; void main() { gl_Position = proj * view * model * vec4(position, 1.0); v_cost = cost; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_obj.frag.glsl ================================================ #version 330 uniform vec4 color; out vec4 outcol; void main() { outcol = color; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_obj.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_vector.frag.glsl ================================================ #version 330 uniform vec4 color; out vec4 outcol; void main() { outcol = color; } ================================================ FILE: assets/test/shaders/pathfinding/demo_0_vector.vert.glsl ================================================ #version 330 layout(location=0) in vec3 position; uniform mat4 model; uniform mat4 view; uniform mat4 proj; void main() { gl_Position = proj * view * model * vec4(position, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_display.frag.glsl ================================================ #version 330 uniform sampler2D color_texture; in vec2 v_uv; out vec4 col; void main() { col = texture(color_texture, v_uv); } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_display.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; layout(location=1) in vec2 uv; out vec2 v_uv; void main() { gl_Position = vec4(position, 0.0, 1.0); v_uv = uv; } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_grid.frag.glsl ================================================ #version 330 out vec4 out_col; void main() { out_col = vec4(0.0, 0.0, 0.0, 0.3); } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_grid.vert.glsl ================================================ #version 330 layout (location = 0) in vec2 position; uniform mat4 model; uniform mat4 view; uniform mat4 proj; void main() { gl_Position = proj * view * model * vec4(position, 0.0, 1.0); } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_obj.frag.glsl ================================================ #version 330 uniform vec4 color; out vec4 outcol; void main() { outcol = color; } ================================================ FILE: assets/test/shaders/pathfinding/demo_1_obj.vert.glsl ================================================ #version 330 layout(location=0) in vec2 position; uniform mat4 model; uniform mat4 view; uniform mat4 proj; void main() { gl_Position = proj * view * model * vec4(position, 0.0, 1.0); } ================================================ FILE: assets/test/textures/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/test/textures" PATTERN "CMakeLists.txt" EXCLUDE ) ================================================ FILE: assets/test/textures/test_animation.sprite ================================================ # Copyright 2021-2021 the openage authors. See copying.md for legal info. # openage sprite definition file version 2 texture 0 "test_texture.texture" scalefactor 1.0 layer 0 mode=loop position=20 time_per_frame=0.125 angle 0 frame 0 0 0 0 0 frame 1 0 0 0 1 frame 2 0 0 0 2 frame 3 0 0 0 3 frame 4 0 0 0 4 frame 5 0 0 0 5 frame 6 0 0 0 6 frame 7 0 0 0 7 frame 8 0 0 0 8 frame 9 0 0 0 9 frame 10 0 0 0 10 frame 11 0 0 0 11 frame 12 0 0 0 12 frame 13 0 0 0 13 frame 14 0 0 0 14 frame 15 0 0 0 15 frame 16 0 0 0 16 frame 17 0 0 0 17 ================================================ FILE: assets/test/textures/test_gaben.sprite ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage sprite definition file version 2 texture 0 "test_gaben.texture" scalefactor 0.2 layer 0 mode=once angle 0 frame 0 0 0 0 0 ================================================ FILE: assets/test/textures/test_gaben.texture ================================================ # Copyright 2023-2023 the openage authors. # openage texture definition file version 1 imagefile "gaben.png" size 719 1073 pxformat rgba8 cbits=True subtex 0 0 719 1073 359 1000 ================================================ FILE: assets/test/textures/test_missing.sprite ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage sprite definition file version 2 texture 0 "test_missing.texture" scalefactor 1.0 layer 0 mode=once angle 0 frame 0 0 0 0 0 ================================================ FILE: assets/test/textures/test_missing.texture ================================================ # Copyright 2023-2023 the openage authors. # openage texture definition file version 1 imagefile "missing.png" size 32 32 pxformat rgba8 cbits=True subtex 0 0 32 32 16 16 ================================================ FILE: assets/test/textures/test_tank.sprite ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage sprite definition file version 2 texture 0 "test_tank.texture" scalefactor 2.0 layer 0 mode=off angle 0 angle 45 angle 90 angle 135 angle 180 angle 225 angle 270 angle 315 frame 0 0 0 0 0 frame 0 45 0 0 1 frame 0 90 0 0 2 frame 0 135 0 0 3 frame 0 180 0 0 4 frame 0 225 0 0 5 frame 0 270 0 0 6 frame 0 315 0 0 7 ================================================ FILE: assets/test/textures/test_tank.texture ================================================ # The image file was adapted from the "German WW2 isometric pixel tanks" sprite pack, # Copyright 2020-2020 by jh2assets. # # It's licensed under the terms of the Creative Commons Attribution v4.0 International license. # (https://creativecommons.org/licenses/by/4.0/) # # The original version can be found at: # https://jimhatama.itch.io/german-ww2-pixel-tanks # # Modifications by us: # - Merged sprites from the "german_panzer4" animation into one spritesheet # # Copyright 2023-2023 the openage authors. # openage texture definition file version 1 imagefile "test_tank.png" size 153 178 pxformat rgba8 cbits=False subtex 109 101 32 45 16 22 subtex 81 55 68 44 34 22 subtex 1 40 78 37 39 18 subtex 82 1 70 52 35 26 subtex 73 101 34 54 17 27 subtex 1 79 70 51 35 25 subtex 1 1 79 37 38 18 subtex 1 132 68 45 34 22 ================================================ FILE: assets/test/textures/test_tank_mirrored.sprite ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage sprite definition file version 2 texture 0 "test_tank.texture" scalefactor 2.0 layer 0 mode=off angle 0 angle 45 angle 90 angle 135 angle 180 angle 225 mirror_from=135 angle 270 mirror_from=90 angle 315 mirror_from=45 frame 0 0 0 0 0 frame 0 45 0 0 1 frame 0 90 0 0 2 frame 0 135 0 0 3 frame 0 180 0 0 4 ================================================ FILE: assets/test/textures/test_terrain.terrain ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage terrain definition file version 2 texture 0 "test_terrain.texture" scalefactor 1.0 layer 0 frame 0 0 0 0 ================================================ FILE: assets/test/textures/test_terrain.texture ================================================ # Copyright 2023-2023 the openage authors. # openage texture definition file version 1 imagefile "test_terrain.png" size 500 500 pxformat rgba8 cbits=True subtex 0 0 500 500 0 0 ================================================ FILE: assets/test/textures/test_terrain2.terrain ================================================ # Copyright 2023-2023 the openage authors. See copying.md for legal info. # openage terrain definition file version 2 texture 0 "test_terrain2.texture" scalefactor 1.0 layer 0 frame 0 0 0 0 ================================================ FILE: assets/test/textures/test_terrain2.texture ================================================ # Copyright 2023-2023 the openage authors. # openage texture definition file version 1 imagefile "test_terrain2.png" size 500 500 pxformat rgba8 cbits=True subtex 0 0 500 500 0 0 ================================================ FILE: assets/test/textures/test_texture.texture ================================================ # The image file was adapted from the "Elemental Ground Monk" sprite pack, # Copyright 2021-2021 by chierit. # # It's licensed under the terms of the Creative Commons Attribution v4.0 International license. # (https://creativecommons.org/licenses/by/4.0/) # # The original version can be found at: # https://chierit.itch.io/elementals-ground-monk # # Modifications by us: # - Extracted sprites from the "3_atk" animation # - Added an outline to the monk with the openage outline colour command (RGBA: 0,0,0,252) # - Changed orange colour (239,105,47,255) with the openage playercolor colour command (RGBA: 239,105,47,254) # # Copyright 2021-2021 the openage authors. # See copying.md for further legal info. # openage texture definition file version 1 imagefile "test_texture.png" size 208 160 pxformat rgba8 cbits=True subtex 0 0 28 39 0 0 subtex 28 0 27 37 0 0 subtex 55 0 36 38 0 0 subtex 91 0 40 36 0 0 subtex 91 36 42 37 0 0 subtex 0 73 33 42 0 0 subtex 33 73 36 46 0 0 subtex 69 73 35 37 0 0 subtex 131 0 28 33 0 0 subtex 133 33 27 45 0 0 subtex 104 78 28 46 0 0 subtex 0 124 47 31 0 0 subtex 160 0 48 41 0 0 subtex 160 41 48 41 0 0 subtex 132 82 48 41 0 0 subtex 47 123 51 37 0 0 subtex 0 41 55 31 0 0 subtex 132 123 48 31 0 0 ================================================ FILE: assets/textures/CMakeLists.txt ================================================ install(DIRECTORY "." DESTINATION "${ASSET_DIR}/textures" PATTERN "CMakeLists.txt" EXCLUDE ) ================================================ FILE: assets/textures/torn_paper_edge.docx ================================================ # x,y,w,h,cx,cy 0,0,512,16,0,0 ================================================ FILE: buildsystem/CheckCompilerFeatures.cmake ================================================ # Copyright 2015-2021 the openage authors. See copying.md for legal info. include(CheckCXXSourceRuns) if(cxx_thread_local IN_LIST CMAKE_CXX_COMPILE_FEATURES) set(HAVE_THREAD_LOCAL_STORAGE true) else() set(HAVE_THREAD_LOCAL_STORAGE false) endif() check_cxx_source_runs(" #include #include class Cat { public: virtual int maunz() const { return 42; }; auto operator <=>(const Cat &other) const = default; }; template concept Meowable = std::same_as || std::derived_from; template int meow(const T &cat) { return cat.maunz(); } class HouseCat : public Cat { public: int maunz() const override { return 1337; }; }; int main() { HouseCat mrr{}; Cat rrarrr{}; return meow(mrr) > meow(rrarrr) ? 0 : 1; } " HAVE_REQUIRED_CXX20_SUPPORT ) if(NOT HAVE_REQUIRED_CXX20_SUPPORT) message(" The compiler doesn't support required C++20 features: * Concepts * Default comparisons The following versions support these features: * clang++ >= 10 * g++ >= 10 * Microsoft Visual Studio 2019 >= 16.8 Please upgrade your compiler to build openage. ") message(FATAL_ERROR "aborting") endif() ================================================ FILE: buildsystem/CheckInSourceBuild.cmake ================================================ # Copyright 2015-2018 the openage authors. See copying.md for legal info. if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) if(ALLOW_IN_SOURCE_BUILD) message("in-source build") else() message(" In-source builds are disallowed. They are a source of infinite pain, - cluttering up the source folder - causing cmake to silently refuse creating out-of-source builds - overwriting the root Makefile (which contains cleaninsourcebuild) To perform an out-of-source build, - mkdir bin; cd bin; cmake .. - or use ./configure, which will do this automatically If you really want to perform an in-source build, use -DALLOW_IN_SOURCE_BUILD=TRUE You need to run 'make cleaninsourcebuild' right now, since this (failed) attempt already produced traces of an in-source build. cmake will attempt in-source-builds instead of regular ones until you clean those traces (remove CMakeCache.txt and CMakeFiles). ") message(FATAL_ERROR "aborting") endif() else() # Check if the source directory has a generated file. if (EXISTS ${CMAKE_SOURCE_DIR}/openage/config.py) # Clean the generated files from the source directory. execute_process( COMMAND git clean -Xf -- libopenage/ openage/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) endif() endif() ================================================ FILE: buildsystem/CheckRuntimeDependencies.cmake ================================================ # Copyright 2015-2023 the openage authors. See copying.md for legal info. # python modules # a list of imported modules may be obtained via # # grep -RE '^ *(import |from [^.])' | cut -d: -f2- | \ # sed 's/^ *//g' | sort -u | grep -v openage set(REQUIRED_PYTHON_MODULES "PIL.Image" "PIL.ImageDraw" "numpy" "pygments" "mako.template" "toml" "lz4") # command-line tools # example: set(REQUIRED_UTILITIES "foobar") # Checks if the specified python module exists # # check_python_module_exists( ) # : The python module. # : TRUE of FALSE based on the check. function(check_python_module_exists MODULE EXISTS) set(STATEMENT "from importlib import import_module; import_module(\"${MODULE}\")") execute_process( COMMAND ${PYTHON} -c "${STATEMENT}" RESULT_VARIABLE PY_RESULT) if(PY_RESULT EQUAL 0) set(${EXISTS} TRUE PARENT_SCOPE) else() set(${EXISTS} FALSE PARENT_SCOPE) endif() endfunction() # loop through all required python modules to find them foreach(_PYTHON_MODULE ${REQUIRED_PYTHON_MODULES}) if("${PY_MOD_${_PYTHON_MODULE}_EXISTS}" STREQUAL "FOUND") continue() endif() check_python_module_exists(${_PYTHON_MODULE} EXISTS) if(EXISTS) message(STATUS "Checking python3 module ${_PYTHON_MODULE} - Success") set(PY_MOD_${_PYTHON_MODULE}_EXISTS "FOUND" CACHE INTERNAL "Python module availability") else() message(FATAL_ERROR "Checking python3 module ${_PYTHON_MODULE} - Not Found") endif() endforeach() # loop through all required utilities to find them foreach(_UTILITY ${REQUIRED_UTILITIES}) if("${UTILITY_${_UTILITY}_EXISTS}" STREQUAL "FOUND") continue() endif() find_program(${_UTILITY}_EXECUTABLE NAMES "${_UTILITY}" ) if(${_UTILITY}_EXECUTABLE) message(STATUS "Checking utility program ${_UTILITY} - Success") set(UTILITY_${_UTILITY}_EXISTS "FOUND" CACHE INTERNAL "Helper program availability") else() message(FATAL_ERROR "Checking utility program ${_UTILITY} - Not Found") endif() endforeach() ================================================ FILE: buildsystem/DependencyFetch.cmake ================================================ # Copyright 2017-2017 the openage authors. See copying.md for legal info. # Fetch project with given name from the Internet. # Basically wraps ExternalProject and does a nested cmake invocation. # # Usage: # fetch_project( # NAME ${projectname} # [DISABLE_UPDATES] # ${locationspecification...} # ) # # Arguments: # NAME -- custom name of the project # DISABLE_UPDATES -- when set, don't do automatic git updates # locationspecification -- passed to ExternalProject_Add # # sets in caller scope: # ${projectname}_SOURCE_DIR -- subproject source directory # ${projectname}_BINARY_DIR -- subproject binary directory # ${projectname}_SUBPROJ_DIR -- everything of the subproject # -- (including src and bin directory) # # You need to specify the location with options of # ExternalProject_Add, for example: # # fetch_project( # NAME yourmom # GIT_REPOSITORY https://github.com/nevergonna/giveyouup # GIT_TAG origin/master # ) # function(fetch_project) cmake_parse_arguments(PROJ "DISABLE_UPDATES" "NAME" "" ${ARGN}) if(PROJ_NAME STREQUAL "") message(FATAL_ERROR "no project name given") endif() set(PROJ_DIR "${CMAKE_BINARY_DIR}/${PROJ_NAME}-external") set(PROJ_DL_DIR "${PROJ_DIR}/dl") set(PROJ_SRC_DIR "${PROJ_DIR}/source") set(PROJ_BIN_DIR "${PROJ_DIR}/bin") set(PROJ_STAMP_DIR "${PROJ_DIR}/stamp") # CLion creates this file for multiple projects file(REMOVE "${PROJ_DL_DIR}/CMakeCache.txt") # create the ExternalProject configuration file # for the nested cmake call. configure_file( "${BUILDSYSTEM_DIR}/templates/ExternalFetch.cmake.in" "${PROJ_DL_DIR}/CMakeLists.txt" @ONLY ) # run cmake to "configure" the external project. # this prepares the download that will be done in the next step. execute_process(COMMAND ${CMAKE_COMMAND} "-G${CMAKE_GENERATOR}" "-DCMAKE_MAKE_PROGRAM:FILE=${CMAKE_MAKE_PROGRAM}" . OUTPUT_QUIET RESULT_VARIABLE config_ok WORKING_DIRECTORY "${PROJ_DL_DIR}" ) if(config_ok) message(FATAL_ERROR "Failed to set up external project ${PROJ_NAME}") endif() # Now actually download the project. # This does not "build" it! The "build" is the download execution. execute_process(COMMAND ${CMAKE_COMMAND} --build . OUTPUT_QUIET RESULT_VARIABLE download_ok WORKING_DIRECTORY "${PROJ_DL_DIR}" ) if(download_ok) message(FATAL_ERROR "Failed to download external project ${PROJ_NAME}.") endif() set(${PROJ_NAME}_SOURCE_DIR "${PROJ_SRC_DIR}" PARENT_SCOPE) set(${PROJ_NAME}_BINARY_DIR "${PROJ_BIN_DIR}" PARENT_SCOPE) set(${PROJ_NAME}_SUBPROJ_DIR "${PROJ_DIR}" PARENT_SCOPE) endfunction() ================================================ FILE: buildsystem/DetectProjectVersion.cmake ================================================ # Copyright 2015-2023 the openage authors. See copying.md for legal info. # Determines the version of the project # stores a full version string with commit hash and number in the variable `PROJECT_VERSION` # If the project version was already defined or previously retrieved, don't bother. if(PROJECT_VERSION) return() endif() # if .git exists, try running git describe if(IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git") message(STATUS "Set PROJECT_VERSION from git.") execute_process( COMMAND git describe --tags --long WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE _RES OUTPUT_VARIABLE PROJECT_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) if(_RES EQUAL 0) set(USED_GIT_VERSION 1) return() endif() endif() # Fallback for downloaded zip's or git unavailability set(PROJECT_VERSION_FILE "${CMAKE_SOURCE_DIR}/openage_version") if(EXISTS ${PROJECT_VERSION_FILE}) file(STRINGS ${PROJECT_VERSION_FILE} FILE_DESCRIBE_VERSION) STRING(REGEX REPLACE "([0-9]+\\.[0-9]+\\.[0-9]+)" "\\1" PROJECT_VERSION "${FILE_DESCRIBE_VERSION}") endif() # Still could not detect the version. Don't worry, raise a warning (shout a curse word?) and move on. if(NOT PROJECT_VERSION) message(WARNING "Could not determine project version.") set(PROJECT_VERSION "0.0") endif() ================================================ FILE: buildsystem/HandleCXXOptions.cmake ================================================ # Copyright 2015-2023 the openage authors. See copying.md for legal info. # sets CXXFLAGS and compiler for the project #TODO: integrate PGO (profile-guided optimization) build include(CheckCXXCompilerFlag) macro(set_compiler_version_flags TYPE MINIMAL FLAGS INVERS EQTYPE) if(${INVERS} CMAKE_CXX_COMPILER_VERSION VERSION_${EQTYPE} ${MINIMAL}) if(${TYPE} STREQUAL "CXX") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") elseif(${TYPE} STREQUAL "EXTRA") set(EXTRA_FLAGS "${EXTRA_FLAGS} ${FLAGS}") else() message(FATAL_ERROR "Invalid compiler flag type specified!") endif() endif() endmacro() macro(set_compiler_greater_flags TYPE MINIMAL FLAGS) set_compiler_version_flags(${TYPE} ${MINIMAL} ${FLAGS} NOT LESS) endmacro() macro(set_compiler_equal_flags TYPE MINIMAL FLAGS) set_compiler_version_flags(${TYPE} ${MINIMAL} ${FLAGS} "" EQUAL) endmacro() macro(set_compiler_flags TYPE FLAGS) set_compiler_version_flags(${TYPE} "0.0" ${FLAGS} "" GREATER) endmacro() macro(test_compiler_flag_apply TYPE FLAG NAME DONTRUN) if(NOT ${DONTRUN}) check_cxx_compiler_flag("${FLAG}" ${NAME}) if(${NAME}) set_compiler_flags(${TYPE} "${FLAG}") endif() endif() endmacro() macro(set_linker_flags FLAGS) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}") set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${FLAGS}") endmacro() macro(set_cxx_optimize_flags FLAGS) set(${BUILD_TYPE_CXX_FLAGS} "${${BUILD_TYPE_CXX_FLAGS}} ${FLAGS}") endmacro() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(NOT MSVC) set(EXTRA_FLAGS "${EXTRA_FLAGS} -Wall -Wextra -pedantic") endif() # set up gold linker features macro(try_enable_gold_linker) # Activate ld.gold instead of the default option(USE_LD_GOLD "Use GNU gold linker" ON) if(USE_LD_GOLD) if(MINGW) execute_process(COMMAND ${CMAKE_CXX_COMPILER} -Wl,--major-image-version,0,--minor-image-version,0 -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) else() execute_process(COMMAND ${CMAKE_CXX_COMPILER} -fuse-ld=gold -Wl,--version ERROR_QUIET OUTPUT_VARIABLE LD_VERSION) endif() if("${LD_VERSION}" MATCHES "GNU gold") set(HAVE_LD_GOLD TRUE) set_linker_flags("-fuse-ld=gold") else() set(HAVE_LD_GOLD FALSE) message(WARNING "GNU gold linker isn't available, using the default system linker.") endif() endif() # do splitdebug by default when in debug mode set(DEBUG_FISSION_DEFAULT OFF) if(HAVE_LD_GOLD AND CMAKE_BUILD_TYPE STREQUAL "Debug") check_cxx_compiler_flag("-gsplit-dwarf" HAVE_GSPLIT_DWARF_SUPPORT) if(HAVE_GSPLIT_DWARF_SUPPORT) set(DEBUG_FISSION_DEFAULT ON) endif() endif() # https://gcc.gnu.org/wiki/DebugFission option(DEBUG_FISSION "Enable Debug Fission" DEBUG_FISSION_DEFAULT) if(DEBUG_FISSION) if(NOT HAVE_LD_GOLD) message(FATAL_ERROR "GNU gold linker required for Debug Fission") endif() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gsplit-dwarf") set_linker_flags("-Wl,--gdb-index") endif() endmacro() # check for compiler versions if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set_compiler_greater_flags("CXX" 4.9 "-fdiagnostics-color=auto") set_compiler_greater_flags("EXTRA" 5.0 "-Wsuggest-override") try_enable_gold_linker() elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set_compiler_flags("EXTRA" "-Wno-gnu-statement-expression") try_enable_gold_linker() if(APPLE) set_compiler_flags("CXX" "-stdlib=libc++") endif() elseif(MSVC) # Don't worry. You're not alone. If you face an issue, just ask. # Enable multi processor compilation on MSVC set_compiler_flags("CXX" "/MP") return() # The following flag specifications don't apply. else() # "Intel", etc.. message(WARNING "Using untested compiler, at least I hope it's free software. Continue on your own, warrior.") endif() # optional code warnings from include-what-you-use if("${CXX_INCLUDE_WHAT_YOU_USE}" STREQUAL "warn") set(CXX_INCLUDE_WHAT_YOU_USE) elseif("${CXX_INCLUDE_WHAT_YOU_USE}" STREQUAL "error") set(CXX_INCLUDE_WHAT_YOU_USE "--error_always") endif() # optimization settings. # TODO: multi-configuration support for xcode, vstudio, ... # the following code just makes sense for single-config # generation, e.g. makefiles. # we'd have to perform the flag generation for other types as well. # these flags will now be extended by the following code. set(CMAKE_CXX_FLAGS_DEBUG "-g") set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG") # If CXX_OPTIMIZATION_LEVEL was not provided, default to auto if("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "") set(CXX_OPTIMIZATION_LEVEL "auto") endif() if("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "auto") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CXX_OPTIMIZATION_LEVEL "g") else() set(CXX_OPTIMIZATION_LEVEL "3") endif() endif() if("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "0") set_cxx_optimize_flags("-O0") elseif("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "1") set_cxx_optimize_flags("-O1") elseif("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "2") set_cxx_optimize_flags("-O2") elseif("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "3") set_cxx_optimize_flags("-O3") elseif("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "g") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set_cxx_optimize_flags("-Og") else() set_cxx_optimize_flags("-O0") endif() elseif("${CXX_OPTIMIZATION_LEVEL}" STREQUAL "max") set_cxx_optimize_flags("-O3 -march=native") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") include(ProcessorCount) ProcessorCount(N) if(NOT N EQUAL 0) set_cxx_optimize_flags("-flto=${N}") set_linker_flags("-flto=${N}") endif() endif() endif() # sanitizing options if(NOT CXX_SANITIZE_MODE) set(CXX_SANITIZE_MODE "none") endif() if("${CXX_SANITIZE_MODE}" STREQUAL "none") # Do nothing if("${CXX_SANITIZE_FATAL}") message(WARNING "CXX_SANITIZE_FATAL is only valid when CXX_SANITIZE_MODE is not none") endif() else() set_compiler_flags("CXX" "-fno-omit-frame-pointer") if("${CXX_SANITIZE_FATAL}" AND "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set_compiler_flags("CXX" "-fno-sanitize-recover") endif() if("${CXX_SANITIZE_MODE}" STREQUAL "yes") set_compiler_flags("CXX" "-fsanitize=address") set_compiler_flags("CXX" "-fsanitize=undefined") elseif("${CXX_SANITIZE_MODE}" STREQUAL "mem") if(NOT APPLE) set_compiler_flags("CXX" "-fsanitize=memory") endif() elseif("${CXX_SANITIZE_MODE}" STREQUAL "thread") if(NOT APPLE) set_compiler_flags("CXX" "-fsanitize=thread") endif() if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set_compiler_flags("CXX" "-fPIC") set_compiler_flags("CXX" "-pie") endif() else() message(WARNING "Unknown sanitizer mode provided: ${CXX_SANITIZE_MODE}") endif() endif() ================================================ FILE: buildsystem/HandlePythonOptions.cmake ================================================ # Copyright 2015-2025 the openage authors. See copying.md for legal info. # finds the python interpreter, install destination and extension flags. # the Python version number requirement is in modules/FindPython_test.cpp find_package(Python ${PYTHON_MIN_VERSION} REQUIRED) find_package(Cython ${CYTHON_MIN_VERSION}) if(NOT CYTHON_FOUND) message("Checking for alternative Cython fallback version (>=${CYTHON_MIN_VERSION_FALLBACK} AND <=${CYTHON_MAX_VERSION_FALLBACK})") find_package(Cython ${CYTHON_MIN_VERSION_FALLBACK} QUIET) if(CYTHON_VERSION VERSION_LESS ${CYTHON_MIN_VERSION} AND CYTHON_VERSION VERSION_GREATER ${CYTHON_MAX_VERSION_FALLBACK}) message(FATAL_ERROR "Cython version ${CYTHON_VERSION} is not compatible") else() message("Compatible Cython version ${CYTHON_VERSION} found") endif() endif() py_get_config_var(EXT_SUFFIX PYEXT_SUFFIX) if(MINGW) string(REGEX REPLACE "dll" "pyd" PYEXT_SUFFIX "${PYEXT_SUFFIX}") endif() # This is the only useful thing after cleaning up what python suggests if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") set(PYEXT_CXXFLAGS "-fwrapv") endif() # numpy deprecated api # http://docs.cython.org/en/latest/src/userguide/source_files_and_compilation.html#configuring-the-c-build if(CYTHON_VERSION VERSION_GREATER_EQUAL 3) set(PYEXT_CXXFLAGS "${PYEXT_CXXFLAGS} -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION") else() # suppress #warning about deprecated numpy api if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(PYEXT_CXXFLAGS "${PYEXT_CXXFLAGS} -Wno-cpp") elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") set(PYEXT_CXXFLAGS "${PYEXT_CXXFLAGS} -Wno-#warnings") endif() endif() # silence cython+python3.8 tp_print deprecation warning # https://github.com/cython/cython/pull/3201 # https://github.com/cython/cython/issues/3474 if(PYTHON_VER VERSION_GREATER_EQUAL 3.8 AND PYTHON_VERSION VERSION_LESS 3.9) set(PYEXT_CXXCLAGS "${PYEXT_CXXCLAGS}" "-Wno-deprecated-declarations") endif() set(PYEXT_LIBRARY "${PYTHON_LIBRARIES}") message("PYTHON_LIBRARIES: " "${PYTHON_LIBRARIES}") #Windows always uses optimized version of Python lib if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") #get index of string "optimized" and increment it by 1 so index points at the path of the optimized lib list(FIND PYEXT_LIBRARY "optimized" _index) if(${_index} GREATER -1) MATH(EXPR _index "${_index}+1") list(GET PYEXT_LIBRARY ${_index} PYEXT_LIBRARY) endif() message("force linking to python release lib, instead of debug lib when cythonising") set(force_optimized_lib_flag "--force_optimized_lib") endif() set(PYEXT_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS};${NUMPY_INCLUDE_DIR}") if(NOT CMAKE_PY_INSTALL_PREFIX) if(MSVC) set(CMAKE_PY_INSTALL_PREFIX "python") else() # get site-packages directory, prepended with cmake's install prefix py_exec("import sys, sysconfig, os; print(os.path.join('${CMAKE_INSTALL_PREFIX}', os.path.relpath(sysconfig.get_path('purelib'), os.path.normpath(sys.prefix))))" PREFIX) set(CMAKE_PY_INSTALL_PREFIX "${PREFIX}") endif() endif() ================================================ FILE: buildsystem/__init__.py ================================================ # Copyright 2015-2020 the openage authors. See copying.md for legal info. """ CMake build helpers; see doc/buildsystem.md for further info. Modules in the 'buildsystem' package are not intended to be installed with openage, or used from outside the build process; otherwise, they would be in the 'openage' package. """ ================================================ FILE: buildsystem/check_py_file_list.py ================================================ # Copyright 2015-2021 the openage authors. See copying.md for legal info. """ Tests whether the files listed via add_py_module are consistent with the .py files in the openage directory. """ import argparse import os import sys def main(): """ CLI entry point """ cli = argparse.ArgumentParser() cli.add_argument('py_file_list', help=( "semicolon-separated list of listed .py files" )) cli.add_argument('py_module_dir', help=( "directory containing the python module to check" )) cli.add_argument('-v', '--verbose', action='store_true', help=( "produce verbose output" )) args = cli.parse_args() openage_dir = os.path.realpath(args.py_module_dir) listed = set() with open(args.py_file_list, encoding='utf8') as fileobj: for filename in fileobj.read().strip().split(';'): filepath = os.path.realpath(os.path.normpath(filename)) if filepath.startswith(openage_dir): listed.add(filepath) elif args.verbose: print("Ignoring " + filepath + " outside " + openage_dir) if args.verbose: print("Files listed in " + args.py_file_list + ":", *sorted(listed), sep='\n\t') actual = set() for dirname, _, files in os.walk(openage_dir): dirname = os.path.realpath(os.path.abspath(dirname)) for filename in files: if filename.endswith('.py'): actual.add(os.path.join(dirname, filename)) if args.verbose: print("Files available:", *sorted(actual), sep='\n\t') success = True for filename in sorted(actual - listed): success = False print("file was not listed via add_py_module: " + os.path.relpath(filename, openage_dir)) for filename in sorted(listed - actual): success = False print("file was listed via add_py_module but does not exist: " + os.path.relpath(filename, openage_dir)) if success: return 0 return 1 if __name__ == '__main__': sys.exit(main()) ================================================ FILE: buildsystem/codecompliance/__init__.py ================================================ # Copyright 2015-2016 the openage authors. See copying.md for legal info. """ Code compliance checker module Integrated into the root Makefile. """ ================================================ FILE: buildsystem/codecompliance/__main__.py ================================================ # Copyright 2014-2025 the openage authors. See copying.md for legal info. """ Entry point for the code compliance checker. """ import argparse import importlib import os import shutil import subprocess import sys from .util import log_setup def parse_args(): """ Returns the raw argument namespace. """ cli = argparse.ArgumentParser() check_types = cli.add_mutually_exclusive_group() check_types.add_argument("--fast", action="store_true", help="do all checks that can be performed quickly") check_types.add_argument("--merge", action="store_true", help="do all checks that are required before merges to master") check_types.add_argument("--all", action="store_true", help="do all checks, even the really slow ones") cli.add_argument("--only-changed-files", metavar='GITREF', help=("slow checks are only done on files that have " "changed since GITREF.")) cli.add_argument("--authors", action="store_true", help=("check whether all git authors are in copying.md. " "repo must be a git repository.")) cli.add_argument("--clang-tidy", action="store_true", help=("Check the C++ code with clang-tidy. Make sure you have build the " "project with ./configure --clang-tidy or have set " "CMAKE_CXX_CLANG_TIDY for your CMake build.")) cli.add_argument("--cppstyle", action="store_true", help="check the cpp code style") cli.add_argument("--cython", action="store_true", help="check if cython is turned off") cli.add_argument("--headerguards", action="store_true", help="check all header guards") cli.add_argument("--legal", action="store_true", help="check whether all sourcefiles have legal headers") cli.add_argument("--filemodes", action="store_true", help=("check whether files in the repo have the " "correct access bits (-> 0644) ")) cli.add_argument("--pylint", action="store_true", help="run pylint on the python code") cli.add_argument("--pystyle", action="store_true", help=("check whether the python code complies with " "(a selected subset of) pep8.")) cli.add_argument("--textfiles", action="store_true", help="check text files for whitespace issues") cli.add_argument("--test-git-change-years", action="store_true", help=("when doing legal checks, test whether the " "copyright year matches the git history.")) cli.add_argument("--fix", action="store_true", help="try to automatically fix the found issues") cli.add_argument("-v", "--verbose", action="count", default=0, help="increase program verbosity") cli.add_argument("-q", "--quiet", action="count", default=0, help="decrease program verbosity") args = cli.parse_args() process_args(args, cli.error) return args def process_args(args, error): """ Sanitizes the given argument namespace, modifying it in the process. Calls error (with a string argument) in case of errors. """ # this method is very flat; artificially nesting it would be bullshit. # pylint: disable=too-many-branches # set up log level log_setup(args.verbose - args.quiet) if args.fast or args.merge or args.all: # enable "fast" tests args.authors = True args.cppstyle = True args.cython = True args.headerguards = True args.legal = True args.filemodes = True args.textfiles = True if args.merge or args.all: # enable tests that are required before merging to master args.pystyle = True args.pylint = True args.test_git_change_years = True if args.all: # enable tests that take a bit longer args.clang_tidy = True if not any((args.headerguards, args.legal, args.authors, args.pystyle, args.cppstyle, args.cython, args.test_git_change_years, args.pylint, args.filemodes, args.textfiles, args.clang_tidy)): error("no checks were specified") has_git = bool(shutil.which('git')) is_git_repo = os.path.exists('.git') if args.only_changed_files and not all((has_git, is_git_repo)): error("can not check only changed files: git is required") if args.authors: if not all((has_git, is_git_repo)): # non-fatal fail print("can not check author list for compliance: git is required") args.authors = False if args.test_git_change_years: if not args.legal: error("--test-git-change-years may only be passed with --legal") if not all((has_git, is_git_repo)): error("--test-git-change-years requires git") if args.pystyle: if not importlib.util.find_spec('pep8') and \ not importlib.util.find_spec('pycodestyle'): error("pep8 or pycodestyle python module " "required for style checking") if args.pylint: if not importlib.util.find_spec('pylint'): error("pylint python module required for linting") if args.clang_tidy: if not shutil.which('clang-tidy'): error("--clang-tidy requires clang-tidy to be installed") def get_changed_files(gitref): """ return a list of changed files """ invocation = ['git', 'diff', '--name-only', '--diff-filter=ACMRTUXB', gitref] try: file_list = subprocess.check_output(invocation) except subprocess.CalledProcessError as exc: raise RuntimeError( "could not determine list of recently-changed files with git" ) from exc return set(file_list.decode('ascii').strip().split('\n')) def main(args): """ Takes an argument namespace as returned by parse_args. Calls find_all_issues(main args, list of files to consider) Returns True if no issues were found. """ if args.only_changed_files: check_files = get_changed_files(args.only_changed_files) else: check_files = None auto_fixes = [] fixes_possible = False issues_count = 0 for title, text, apply_fix in find_all_issues(args, check_files): issues_count += 1 print(f"\x1b[33;1mWARNING\x1b[m {title}: {text}") if apply_fix: fixes_possible = True if args.fix: print(" This will be fixed automatically.") auto_fixes.append(apply_fix) else: print(" This can be fixed automatically.") # nicely seperate warnings print() if args.fix and auto_fixes: print(f"\x1b[33;1mApplying {len(auto_fixes):d} automatic fixes...\x1b[m") for auto_fix in auto_fixes: print(auto_fix()) issues_count -= 1 print() if issues_count > 0: plural = "s" if issues_count > 1 else "" if args.fix and auto_fixes: remainfound = f"remain{plural}" else: remainfound = ("were" if issues_count > 1 else "was") + " found" print(f"==> \x1b[33;1m{issues_count} issue{plural}\x1b[m {remainfound}.") if not args.fix and fixes_possible: print("When invoked with --fix, I can try " "to automatically resolve some of the issues.\n") return issues_count == 0 def find_all_issues(args, check_files=None): """ Invokes all the individual issue checkers, and yields their returned issues. If check_files is not None, all other files are ignored during the more resource-intense checks. That is, check_files is the set of files to verify. Yields tuples of (title, text) that are displayed as warnings. """ # pylint: disable=too-many-function-args, no-value-for-parameter # no-value-for-parameter has to be used because pylint is dumb if args.headerguards: from .headerguards import find_issues yield from find_issues('libopenage') if args.authors: from .authors import find_issues yield from find_issues() if args.pystyle: from .pystyle import find_issues yield from find_issues(check_files, ('openage', 'buildsystem', 'etc/gdb_pretty')) if args.cython: from buildsystem.codecompliance.cython import find_issues yield from find_issues(check_files, ('openage',)) if args.cppstyle: from .cppstyle import find_issues yield from find_issues(check_files, ('libopenage',)) if args.pylint: from .pylint import find_issues yield from find_issues(check_files, ('openage', 'buildsystem', 'etc/gdb_pretty')) if args.textfiles: from .textfiles import find_issues yield from find_issues( ('openage', 'libopenage', 'buildsystem', 'doc', 'legal', 'etc/gdb_pretty'), ('.pxd', '.pyx', '.pxi', '.py', '.h', '.cpp', '.template', '', '.txt', '.md', '.conf', '.cmake', '.in', '.yml', '.supp', '.desktop')) if args.legal: from .legal import find_issues yield from find_issues(check_files, ('openage', 'buildsystem', 'libopenage', 'etc/gdb_pretty'), args.test_git_change_years) if args.filemodes: from .modes import find_issues yield from find_issues(check_files, ('openage', 'buildsystem', 'libopenage', 'etc/gdb_pretty')) if args.clang_tidy: from .clangtidy import find_issues yield from find_issues(check_files, ('libopenage', )) if __name__ == '__main__': if main(parse_args()): sys.exit(0) else: sys.exit(1) ================================================ FILE: buildsystem/codecompliance/authors.py ================================================ # Copyright 2014-2024 the openage authors. See copying.md for legal info. """ Checks whether all authors are properly listed in copying.md. """ import re import logging from .util import Strlazy def deobfuscate_email(string): """ Should reveal the original email address passed into obfuscate_email deobfuscate_email('first dawt last+tag à gmail dawt com') = 'first.last+tag@gmail.com' """ replacements = { ' dawt ': '.', ' à ': '@' } for key, value in replacements.items(): string = string.replace(key, value) return string def get_author_emails_copying_md(): """ yields all emails from the author table in copying.md they must be part of a line like | name | nick | email | """ with open("copying.md", encoding='utf8') as fobj: for line in fobj: match = re.match(r"^.*\|[^|]*\|[^|]*\|([^|]+)\|.*$", line) if not match: continue email = match.group(1).strip() if 'à' in email: email = deobfuscate_email(email) if not any(email.startswith(prefix) for prefix in ("E-Mail", "-" * 15))\ and '@' not in email: raise ValueError(f"no @ or à was found in email: {email}") yield email def get_author_emails_git_shortlog(exts): """ yields emails of all authors that have authored any of the files ending in exts (plus their templates) parses the output of git shortlog -sne """ from subprocess import Popen, PIPE invocation = ['git', 'shortlog', '-sne', '--'] for ext in exts: invocation.append(f"*{ext}") invocation.append(f"*{ext}.in") invocation.append(f"*{ext}.template") with Popen(invocation, stdout=PIPE) as invoc: output = invoc.communicate()[0] for line in output.decode('utf-8', errors='replace').split('\n'): match = re.match("^ +[0-9]+\t[^<]*\\<(.*)\\>$", line) if match: yield match.group(1).lower() def find_issues(): """ compares the output of git shortlog -sne to the authors table in copying.md prints all discrepancies, and returns False if one is detected. """ relevant_exts = ('.cpp', '.h', '.py', '.pyi', '.pyx', '.cmake', '.qml') copying_md_emails = set(get_author_emails_copying_md()) logging.debug("scanned authors in copying.md:\n%s", Strlazy(lambda: f"{chr(10).join(sorted(copying_md_emails))}")) git_shortlog_emails = set(get_author_emails_git_shortlog(relevant_exts)) logging.debug("scanned authors from git shortlog:\n%s", Strlazy(lambda: f"{chr(10).join(sorted(git_shortlog_emails))}")) # look for git emails that are unlisted in copying.md for email in git_shortlog_emails - copying_md_emails: if email in {'coop@sft.mx', '?'}: continue yield ( "author inconsistency", f"{email}\n\temail appears in git log, but not in copying.md or .mailmap", None ) ================================================ FILE: buildsystem/codecompliance/clangtidy.py ================================================ # Copyright 2024-2024 the openage authors. See copying.md for legal info. """ Checks clang-tidy errors on cpp files """ import subprocess from .cppstyle import filter_file_list from .util import findfiles def find_issues(check_files, dirnames): """ Invoke clang-tidy to check C++ files for issues. Yields issues found by clang-tidy in real-time. """ # Specify the checks to include # 4 checks we focus on checks_to_include = [ 'clang-analyzer-*', 'bugprone-*', 'concurrency-*', 'performance-*' ] # Create the checks string checks = ', '.join(checks_to_include) # Invocation command invocation = ['clang-tidy', f'-checks=-*,{checks}'] # Use utility functions from util.py and cppstyle.py if check_files is not None: filenames = list(filter_file_list(check_files, dirnames)) else: filenames = list(filter_file_list(findfiles(dirnames), dirnames)) if not filenames: print("No files to check.") return # No files to check for filename in filenames: # Run clang-tidy for each file print(f"Starting clang-tidy check on file: {filename}") try: with subprocess.Popen( invocation + [filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) as process: # Stream output in real-time while True: output = process.stdout.readline() if output: yield ("clang-tidy output", output.strip(), None) elif process.poll() is not None: break # Capture remaining errors (if any) for error_line in process.stderr: yield ("clang-tidy error", error_line.strip(), None) # Handle exception except subprocess.SubprocessError as exc: yield ( "clang-tidy error", f"An error occurred while running clang-tidy on {filename}: {str(exc)}", None ) ================================================ FILE: buildsystem/codecompliance/cppstyle.py ================================================ # Copyright 2015-2017 the openage authors. See copying.md for legal info. """ Checks some code style rules for cpp files. """ import re from .util import findfiles, readfile, issue_str_line # spaces missing in `if () {` and `for`, `while`, ... MISSING_SPACES_RE = re.compile( # on of the folowing, the first group is used for the column where # the pointer is going to show r"(?:" # a ) folowed by a { without a space between r"(?:\)(\{))|" # a if/for/while folowed by a ( without a space between r"(?:\s+(?:if|for|while)(\())" r")" ) # extra spaces before a ; EXTRA_SPACES_RE = re.compile( # on of the folowing, the first group is used for the column where # the pointer is going to show r"(?:" # a space before a ";" r"(?:(\s+);)" r")" ) # indentation fails looking at a file INDENT_FAIL_RE = re.compile( # leading whitespace fail r"(?:" r"(\n\t*[ ]+\t+)" # \n tab* space+ tab+ r")" ) # we need both because we look at the file first. # indentation fails when looking at a line INDENT_FAIL_LINE_RE = re.compile( # leading whitespace fail r"(?:" r"(^\t*[ ]+\t+)" # tab* space+ tab+ r")" ) def filter_file_list(check_files, dirnames): """ Yields all those files in check_files that are in one of the directories and end in '.cpp' or '.h' and some other conditions. """ for filename in check_files: if not (filename.endswith('.cpp') or filename.endswith('.h')): continue if filename.endswith('.gen.h') or filename.endswith('.gen.cpp'): # TODO all this for now, until someone fixes the codegen. continue if any(filename.startswith(dirname) for dirname in dirnames): yield filename def find_issues(check_files, dirnames): """ Finds all issues in the given directories (filtered by check_files). """ if check_files is not None: filenames = filter_file_list(check_files, dirnames) else: filenames = filter_file_list(findfiles(dirnames), dirnames) for filename in filenames: data = readfile(filename) analyse_each_line = False if MISSING_SPACES_RE.search(data) or\ EXTRA_SPACES_RE.search(data) or\ INDENT_FAIL_RE.search(data): analyse_each_line = True # if there are possible issues perform a per line analysis if analyse_each_line: yield from find_issues_with_lines(data, filename) def find_issues_with_lines(data, filename): """ Checks a file for issues per line """ for num, line in enumerate(data.splitlines(True), start=1): match = MISSING_SPACES_RE.search(line) if match: start = match.start(1) + match.start(2) end = start + 1 yield issue_str_line("Missing space", filename, line, num, (start, end)) match = EXTRA_SPACES_RE.search(line) if match: yield issue_str_line("Extra space", filename, line, num, (match.start(1), match.end(1))) match = INDENT_FAIL_LINE_RE.search(line) if match: yield issue_str_line("Wrong indentation", filename, line, num, (match.start(1), match.end(1))) ================================================ FILE: buildsystem/codecompliance/cython.py ================================================ # Copyright 2021-2021 the openage authors. See copying.md for legal info. """ Verifies that Cython directives for profiling are deactivated. """ import re from buildsystem.codecompliance.util import issue_str_line from .util import findfiles, readfile GLOBAL_PROFILE_DIREC = re.compile(( # global profiling directive for a file r"^(# cython: .*(profile=True|linetrace=True).*\n)" )) FUNC_PROFILE_DIREC = re.compile(( # profiling for single functions r"@cython\.profile\(True\)" )) def filter_file_list(check_files, dirnames): """ Yields all those files in check_files that are in one of the directories and end in '.py'x. """ for filename in check_files: if not filename.endswith('.pyx'): continue if any(filename.startswith(dirname) for dirname in dirnames): yield filename def find_issues(check_files, dirnames): """ Finds all issues in the given directories (filtered by check_files). """ if check_files: filenames = filter_file_list(check_files, dirnames) else: filenames = findfiles(dirnames, ('.pyx',)) for filename in filenames: data = readfile(filename) for num, line in enumerate(data.splitlines(True), start=1): match = GLOBAL_PROFILE_DIREC.match(line) if match: yield issue_str_line("cython profiling activated in header", filename, line, num, (match.start(1), match.end(1))) match = FUNC_PROFILE_DIREC.search(line) if match: yield issue_str_line("cython function profiling activated in file", filename, line, num, (match.start(0), match.end(0))) ================================================ FILE: buildsystem/codecompliance/headerguards.py ================================================ # Copyright 2014-2021 the openage authors. See copying.md for legal info. """ Verifies the guard macros of all C++ header files. """ import re from .util import findfiles, readfile class HeaderIssue(Exception): """ Some issue was detected with the Header guard. """ GUARD_RE = re.compile(( # allow any number of comments or empty lines "^(\\n|(#|//).*\\n)*" # the header guard "#pragma once\n" )) NO_GUARD_REQUIRED_RE = re.compile(( # allow any number of comments or empty lines "^(\\n|(#|//).*\\n)*" # require comment "has no header guard" "(#|//) has no header guard:" )) def find_issues(dirname): """ checks all headerguards in header files in the cpp folders. """ for fname in findfiles((dirname,), ('.h',)): try: data = readfile(fname) if NO_GUARD_REQUIRED_RE.match(data): # this file needs no header guard, for the reason # detailed in the comment. continue match = GUARD_RE.match(data) if not match: raise HeaderIssue("No valid header guard found (e.g. #pragma once)") except HeaderIssue as exc: yield (f"header guard issue in {fname}", exc.args[0], None) ================================================ FILE: buildsystem/codecompliance/legal.py ================================================ # Copyright 2014-2023 the openage authors. See copying.md for legal info. """ Checks the legal headers of all files. """ from datetime import date import re from subprocess import Popen, PIPE from .util import findfiles, readfile, writefile, has_ext, SHEBANG OPENAGE_AUTHORS = ( "Copyright (?P\\d{4})-(?P\\d{4}) the openage authors\\." ) OPENAGE_AUTHORTEMPLATE = ( "Copyright {crstart}-{crend} the openage authors." ) NATIVELEGALHEADER = re.compile( "^" # Allow shebang line, followed by an optional empty line. "(" + SHEBANG + ")?" # Next line must be the copyright line. "(#|//) " + OPENAGE_AUTHORS + " See copying\\.md for legal info\\.\n" ) THIRDPARTYLEGALHEADER = re.compile( "^" # 3rd-party copyright/license "(#|//) This file (was (taken|adapted)|contains (data|code)) from .*\n" "(#|//) Copyright \\d{4}-\\d{4} .*\n" "(#|//) .*license.*\n" # any number of lines containing further 3rd-party copyright info "((#|//) .*\\n)*" # the openage copyright "(#|//) (Modifications|Other (data|code)|Everything else) " + OPENAGE_AUTHORS + "\n" "(#|//) See copying\\.md for further legal info\\.\n") # Empty files (consisting of only comments) don't require a legal header. EMPTYFILE = re.compile("^(((#|//) .*)?\n)*$") # cython-generated files CYTHONGENERATED = re.compile("^[^\\n]*(Generated by Cython |failed Cython compilation.)") # all those files will be checked. EXTENSIONS_REQUIRING_LEGAL_HEADERS = { '.h', '.cpp', '.py', '.pyx', '.pxi', '.cmake', '.h.in', '.cpp.in', '.py.in', '.h.template', '.cpp.template', '.py.template', '.qml' } def get_git_change_year(filename): """ Returns git-log's opinion on when the file was last changed. """ invocation = [ 'git', 'log', '-1', '--format=%ad', '--date=short', '--no-merges', '--', filename ] with Popen(invocation, stdout=PIPE) as proc: output = proc.communicate()[0].decode('utf-8', errors='ignore').strip() if proc.returncode != 0 or not output: # git doesn't know about the file return None return int(output[:4]) def match_legalheader(data): """ Tests whether data matches any of the regular expressions, and returns a tuple of (matching header regex, match). """ for hdr in (NATIVELEGALHEADER, THIRDPARTYLEGALHEADER, EMPTYFILE, CYTHONGENERATED): match = re.match(hdr, data) if match is not None: return hdr, match raise ValueError("no match found") def create_year_fix(filename, file_content, expected_end_year, found_start_year, headertype): """ Create a function that, when called, fixes the copyright header. """ # check if a fix can be created if headertype not in {NATIVELEGALHEADER, THIRDPARTYLEGALHEADER}: return None def year_fix_function(): """ Store the file with correct copyright years. """ fixed_file, success = re.subn( OPENAGE_AUTHORS, OPENAGE_AUTHORTEMPLATE.format(crstart=found_start_year, crend=expected_end_year), file_content ) if not success: raise ValueError("copyright year fix did not suceeed") writefile(filename, fixed_file) return f"Copyright for {filename} was fixed." return year_fix_function def test_headers(check_files, paths, git_change_years, third_party_files): """ Tests all in-sourcefile legal headers. """ if not git_change_years: print("warning: I won't check if the copyright matches the git history.") print(" Run with --test-git-change-years to enable the check.") # determine all uncommited files from git. # those definitely need the current year in the copyright message. with Popen(['git', 'diff', '--name-only', 'HEAD'], stdout=PIPE) as proc: uncommited = set(proc.communicate()[0].decode('ascii').strip().split('\n')) current_calendar_year = date.today().year for filename in findfiles(paths, EXTENSIONS_REQUIRING_LEGAL_HEADERS): try: file_content = readfile(filename) headertype, match = match_legalheader(file_content) except ValueError: yield ( "Legal header missing or invalid", (filename + "\nSee copying.md for a template"), None ) continue if headertype is THIRDPARTYLEGALHEADER: third_party_files.add(filename) try: found_start_year = int(match.group('crstart')) found_end_year = int(match.group('crend')) except IndexError: # this header type has/needs no copyright years # (e.g. empty file) continue expected_end_year = None if filename in uncommited: expected_end_year = current_calendar_year elif git_change_years: if check_files is None or filename in check_files: expected_end_year = get_git_change_year(filename) if expected_end_year is None: continue if found_end_year != expected_end_year: fix = create_year_fix( filename, file_content, expected_end_year, found_start_year, headertype ) yield ( "Bad copyright year", (filename + "\n" + f"\tExpected {expected_end_year}\n" + f"\tFound {found_end_year}"), fix ) def find_issues(check_files, paths, git_change_years=False): """ Tests all source files for the required legal headers. """ third_party_files = set() yield from test_headers( check_files, paths, git_change_years, third_party_files) # test whether all third-party files are listed in copying.md listed_files = set() for line in readfile('copying.md').split('\n'): match = re.match("^ - `([^`]+)`.*$", line) if not match: continue filename = match.group(1) listed_files.add(filename) # file listed, but has no 3rd-party header? for filename in sorted(listed_files - third_party_files): if has_ext(filename, EXTENSIONS_REQUIRING_LEGAL_HEADERS): yield ( "third-party file listing issue", (f"{filename}\n\tlisted in copying.md, but has no " "third-party license header."), None ) # file has 3rd-party header, but is not listed? for filename in sorted(third_party_files - listed_files): yield ( "third-party file listing issue", (f"{filename}\n\thas a third-party license header, but isn't " "listed in copying.md"), None ) ================================================ FILE: buildsystem/codecompliance/modes.py ================================================ # Copyright 2016-2022 the openage authors. See copying.md for legal info. """ Checks the mode of all files and prevents executable source files. """ import re import pathlib import stat from .util import findfiles, SHEBANG SHEBANG_RE = re.compile("^" + SHEBANG) EXTENSIONS_NO_X_BIT = { '.h', '.cpp', '.py', '.pyx', '.pxi', '.cmake', '.h.in', '.cpp.in', '.py.in', '.h.template', '.cpp.template', '.py.template', '.qml' } EXTENSIONS_SHEBANG_XBIT = { '.sh', '.py' } def check_mode(filename): """ Test if the the file has no executable bit set. """ path = pathlib.Path(filename) filemode = path.stat().st_mode x_ok = False if filemode & (stat.S_IXGRP | stat.S_IXOTH | stat.S_IXUSR): if path.suffix in EXTENSIONS_SHEBANG_XBIT: # if the file is allowed to have a shebang, # allow its executable bit if it actually has a shebang with path.open(encoding='utf-8') as file: firstline = file.readline() if SHEBANG_RE.match(firstline): x_ok = True if not x_ok: raise ValueError(f'file {filename} is executable') def find_issues(check_files, paths): """ Check all source files for their required filesystem bits. """ for filename in findfiles(paths, EXTENSIONS_NO_X_BIT): if check_files and filename not in check_files: continue try: check_mode(filename) except ValueError as exc: yield ("wrong file access bits", str(exc), None) continue ================================================ FILE: buildsystem/codecompliance/pylint.py ================================================ # Copyright 2015-2023 the openage authors. See copying.md for legal info. """ Checks the Python modules with pylint. """ from pylint import lint from .pystyle import filter_file_list from .util import findfiles def find_pyx_modules(dirnames): """ Yields the names of all .pyx modules. """ for pyx_file in findfiles(dirnames, [".pyx"]): yield pyx_file.replace('/', '.')[:-len(".pyx")] def find_issues(check_files, dirnames): """ Invokes the external utility. """ invocation = ['--rcfile=etc/pylintrc', '--reports=n'] from multiprocessing import cpu_count invocation.append(f"--jobs={cpu_count():d}") if check_files is None: invocation.extend(dirnames) else: check_files = list(filter_file_list(check_files, dirnames)) if not check_files: return invocation.extend(check_files) try: lint.Run(invocation) except SystemExit as exc: error_count = exc.args[0] if error_count != 0: if check_files is None: msg = f"python code is noncompliant: {error_count:d}" else: msg = ("false positives may result from not checking the " "entire codebase") yield "linting issue", msg, None ================================================ FILE: buildsystem/codecompliance/pystyle.py ================================================ # Copyright 2014-2018 the openage authors. See copying.md for legal info. """ Checks PEP8 compliance, with some exceptions. """ try: from pycodestyle import StyleGuide except ImportError: from pep8 import StyleGuide # these errors will be ignored by pep8 IGNORE_ERRORS = ( "E221", # multiple spaces before operator "E241", # multiple spaces after ',' "E251", # unexpected spaces around keyword / parameter equals "E501", # line too long ) def filter_file_list(check_files, dirnames): """ Yields all those files in check_files that are in one of the directories and end in '.py'. """ for filename in check_files: if not filename.endswith('.py'): continue if any(filename.startswith(dirname) for dirname in dirnames): yield filename def find_issues(check_files, dirnames): """ Finds all issues in the given directories (filtered by check_files). """ checker = StyleGuide() checker.options.ignore = IGNORE_ERRORS filenames = dirnames if check_files is not None: filenames = filter_file_list(check_files, dirnames) report = checker.check_files(filenames) if report.messages: yield ("style issue", "python code violates pep8", None) ================================================ FILE: buildsystem/codecompliance/textfiles.py ================================================ # Copyright 2015-2019 the openage authors. See copying.md for legal info. """ Checks some general whitespace rules and the encoding for text files. """ import re from .util import findfiles, readfile, has_ext, issue_str_line, BADUTF8FILES TRAIL_WHITESPACE_RE = re.compile(( # trailing whitespace r"( |\t)+\n" )) IMMEDIATE_TODO_RE = re.compile( "as" + "df", re.IGNORECASE # pylint: disable=no-member ) def find_issues(dirnames, exts): """ Checks all files ending in exts in dirnames. """ for filename in findfiles(dirnames, exts): data = readfile(filename) analyse_each_line = False if filename.endswith('.gen.h') or filename.endswith('.gen.cpp'): # TODO all this for now, until someone fixes the codegen. continue if filename.startswith('openage/') and filename.endswith('.cpp'): # allow issues for Cython-generated files. continue if '\r\n' in data: yield "Windows EOL format", filename, None if data.endswith('\n\n'): yield "Trailing newline at file end", filename, None if data and not data.endswith('\n'): yield "File does not end in '\\n'", filename, None if has_ext(filename, ('.py', '.pyx', '.pxd')): if '\t' in data: yield "File contains tabs", filename, None if TRAIL_WHITESPACE_RE.search(data) or IMMEDIATE_TODO_RE.search(data): analyse_each_line = True # If there are possible issues, perform an in-depth analysis. if analyse_each_line: yield from find_issues_with_lines(filename) for filename in BADUTF8FILES: yield "Not valid UTF-8", filename def find_issues_with_lines(filename): """ Checks a file for issues per line. """ data = readfile(filename) for num, line in enumerate(data.splitlines(True), start=1): match = TRAIL_WHITESPACE_RE.search(line) if match: yield issue_str_line("Trailing whitespace", filename, line, num, (match.start(1), match.end(1))) match = IMMEDIATE_TODO_RE.search(line) if match: yield issue_str_line("Found 'as" "df', indicating an immediate TODO", filename, line, num, (match.start(), match.end())) ================================================ FILE: buildsystem/codecompliance/util.py ================================================ # Copyright 2014-2023 the openage authors. See copying.md for legal info. """ Some utilities. """ import logging import os SHEBANG = "#!/.*\n(#?\n)?" FILECACHE = {} BADUTF8FILES = set() def log_setup(setting, default=1): """ Perform setup for the logger. Run before any logging.log thingy is called. if setting is 0: the default is used, which is WARNING. else: setting + default is used. """ levels = (logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG, logging.NOTSET) factor = clamp(default + setting, 0, len(levels) - 1) level = levels[factor] logging.basicConfig(level=level, format="[%(asctime)s] %(message)s") logging.captureWarnings(True) def clamp(number, smallest, largest): """ return number but limit it to the inclusive given value range """ return max(smallest, min(number, largest)) class Strlazy: # pylint: disable=too-few-public-methods """ to be used like this: logging.debug("rolf %s", strlazy(lambda: do_something())) so do_something is only called when the debug message is actually printed do_something could also be an f-string. """ def __init__(self, fun): self.fun = fun def __str__(self): return self.fun() def has_ext(fname, exts): """ Returns true if fname ends in any of the extensions in ext. """ for ext in exts: if ext == '': if os.path.splitext(fname)[1] == '': return True elif fname.endswith(ext): return True return False def readfile(filename): """ reads the file, and returns it as a str object. if the file has already been read in the past, returns it from the cache. """ if filename not in FILECACHE: with open(filename, 'rb') as fileobj: data = fileobj.read() try: data = data.decode('utf-8') except UnicodeDecodeError: data = data.decode('utf-8', errors='replace') BADUTF8FILES.add(filename) FILECACHE[filename] = data return FILECACHE[filename] def writefile(filename, new_content): """ writes the file and update it in the cache. """ if filename in BADUTF8FILES: raise ValueError(f"{filename}: cannot write due to utf8-errors.") with open(filename, 'w', encoding='utf8') as fileobj: fileobj.write(new_content) FILECACHE[filename] = new_content def findfiles(paths, exts=None): """ yields all files in paths with names ending in an ext from exts. If exts is None, all extensions are accepted. hidden dirs and files are ignored. """ for path in paths: for filename in os.listdir(path): if filename.startswith('.'): continue filename = os.path.join(path, filename) if os.path.isdir(filename): yield from findfiles((filename,), exts) continue if exts is None or has_ext(filename, exts): yield filename def issue_str(title, filename, fix=None): """ Creates a formated (title, text) desciption of an issue. TODO use this function and issue_str_line for all issues, so the format can be easily changed (exta text, colors, etc) """ return (title, filename, fix) # gread hint, pylint. thank you so much. # pylint: disable=too-many-arguments def issue_str_line(title, filename, line, line_number, highlight, fix=None): """ Creates a formated (title, text) desciption of an issue with information about the location in the file. line: line content line_number: line id in the file highlight: a tuple of (start, end), where start: match start in the line end: match end in the line """ start, end = highlight start += 1 line = line.replace("\n", "").replace("\t", " ") return ( title, ( filename + "\n" "\tline: " + str(line_number) + "\n" # line number "\tat: '" + line + "'\n" # line content "\t " + (' ' * start) + # mark position with ^ "\x1b[32;1m^" + ("~" * (end - start)) + "\x1b[m" ), fix ) ================================================ FILE: buildsystem/codegen.cmake ================================================ # Copyright 2014-2025 the openage authors. See copying.md for legal info. # set CODEGEN_SCU_FILE to the absolute path to SCU file macro(get_codegen_scu_file) set(CODEGEN_SCU_FILE "${CMAKE_BINARY_DIR}/libopenage/codegen_scu.gen.cpp") endmacro() function(codegen_run) # this function must be called once all required assets have been created, but before the executable is finalized. # make sure this function gets called only once get_property(codegen_run GLOBAL PROPERTY SFT_CODEGEN_HAS_BEEN_RUN) if(codegen_run) message(FATAL_ERROR "codegen has already been run") endif() set_property(GLOBAL PROPERTY SFT_CODEGEN_HAS_BEEN_RUN 1) set(CODEGEN_INVOCATION "${PYTHON}" -m openage codegen "--input-dir=${CMAKE_SOURCE_DIR}" "--output-dir=${CMAKE_BINARY_DIR}" "--generated-list-file=${CMAKE_BINARY_DIR}/codegen_generated_files" "--depend-list-file=${CMAKE_BINARY_DIR}/codegen_depends") execute_process(COMMAND ${CODEGEN_INVOCATION} --mode=dryrun WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" RESULT_VARIABLE COMMAND_RESULT ) if(NOT ${COMMAND_RESULT} EQUAL 0) message(FATAL_ERROR "failed to get target list from codegen invocation") endif() FILE(READ "${CMAKE_BINARY_DIR}/codegen_generated_files" CODEGEN_TARGET_FILES) FILE(READ "${CMAKE_BINARY_DIR}/codegen_depends" CODEGEN_DEPENDS) STRING(REGEX REPLACE "\n" ";" CODEGEN_TARGET_FILES ${CODEGEN_TARGET_FILES}) STRING(REGEX REPLACE "\n" ";" CODEGEN_DEPENDS ${CODEGEN_DEPENDS}) # as the codegen creates all files at once, # let the buildsystem only depend on this single dummy file. # otherwise the codegen invocation would be done for each generated source. set(CODEGEN_TIMEFILE "${CMAKE_BINARY_DIR}/codegen_timefile") add_custom_command( OUTPUT "${CODEGEN_TIMEFILE}" BYPRODUCTS ${CODEGEN_TARGET_FILES} COMMAND ${CODEGEN_INVOCATION} --mode=codegen "--touch-file-on-cache-change=${CMAKE_CURRENT_LIST_FILE}" --force-rerun-on-generated-list-change COMMAND "${CMAKE_COMMAND}" -E touch "${CODEGEN_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${CODEGEN_DEPENDS} COMMENT "openage.codegen: generating c++ code" ) add_custom_target(cppgen DEPENDS "${CODEGEN_TIMEFILE}" ) add_custom_target(cleancodegen COMMAND ${CODEGEN_INVOCATION} --mode=clean COMMAND "${CMAKE_COMMAND}" -E remove "${CODEGEN_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" ) get_codegen_scu_file() get_filename_component(CODEGEN_SCU_DIR ${CODEGEN_SCU_FILE} DIRECTORY) set(CODEGEN_SCU_CONTENT "// Generated by codegen.\n\n") list(SORT CODEGEN_TARGET_FILES) foreach(target ${CODEGEN_TARGET_FILES}) if("${target}" MATCHES "\\.cpp$") file(RELATIVE_PATH target_rel ${CODEGEN_SCU_DIR} ${target}) string(APPEND CODEGEN_SCU_CONTENT "#include \"${target_rel}\"\n") endif() endforeach() write_on_change(${CODEGEN_SCU_FILE} "${CODEGEN_SCU_CONTENT}") endfunction() ================================================ FILE: buildsystem/compilepy.py ================================================ # Copyright 2015-2022 the openage authors. See copying.md for legal info. """ Compiles python modules with cpython to pyc/pyo files. """ import argparse import importlib.util import os import py_compile import shutil import sys def clone_file_to_dir(sourcefile, input_dir, output_dir): """ Make a copy of sourcefile from input_dir to output_dir Try hardlinking first; on failure fallback to copy. Caveat: source files already in output_dir are not cloned. :return: the path of file created in output_dir """ if os.path.samefile(input_dir, output_dir) or sourcefile.startswith(output_dir): return sourcefile relsrcpath = os.path.relpath(sourcefile, input_dir) targetfile = os.path.join(output_dir, relsrcpath) if os.path.exists(targetfile) and os.path.samefile(sourcefile, targetfile): return targetfile os.makedirs(os.path.dirname(targetfile), exist_ok=True) # try to hardlink try: os.link(sourcefile, targetfile) return targetfile except OSError: pass # we don't try to symlink, because then Python may "intelligently" # determine the actual location, and refuse to work. # fallback to copying the file shutil.copy(sourcefile, targetfile) return targetfile def main(): """ CLI entry point """ cli = argparse.ArgumentParser() cli.add_argument("pymodule_list_file", help=( "semicolon-separated list of all modules that shall be compiled" )) cli.add_argument("input_dir", help=( "base directory where files from the above list are in." )) cli.add_argument("output_dir", help=( "base directory where output files will be created." )) cli.add_argument("--print-output-paths-only", action="store_true", help=( "print the paths of the compiled output files and exit" )) args = cli.parse_args() with open(args.pymodule_list_file, encoding='utf8') as fileobj: modules = fileobj.read().strip().split(';') if modules == ['']: modules = [] if not os.path.isdir(args.output_dir): cli.error(f"not a directory: '{args.output_dir}'") all_output_files = [] to_compile = [] for module in modules: # the module is required in the output directory sourcefile = clone_file_to_dir(module, args.input_dir, args.output_dir) outputfile = importlib.util.cache_from_source(sourcefile) all_output_files.append(outputfile) if os.path.exists(outputfile): if os.path.getmtime(outputfile) >= os.path.getmtime(sourcefile): continue os.remove(outputfile) to_compile.append((sourcefile, outputfile)) if args.print_output_paths_only: print(';'.join(all_output_files)) sys.exit(0) maxwidth = len(str(len(to_compile))) for idx, (module, outputfile) in enumerate(to_compile): try: print(f"[{idx+1:{maxwidth}}/{len(to_compile)}] " f"Compiling {module} to {outputfile}") py_compile.compile(module, cfile=outputfile, doraise=True) except py_compile.PyCompileError as exc: print(f"FAILED to compile '{exc.file}':") print(exc.msg) sys.exit(1) if __name__ == '__main__': main() ================================================ FILE: buildsystem/cpp.cmake ================================================ # Copyright 2014-2018 the openage authors. See copying.md for legal info. # declare a new 'empty' executable file. # you need to use add_sources to add source files to it, and finalize_binary to finalize it. # then you can add libraries, include dirs, etc. function(declare_binary target_name output_name type) # create the executable if(type STREQUAL "executable") add_executable("${target_name}" ${sources}) elseif(type STREQUAL "library") add_library("${target_name}" SHARED ${sources}) endif() set_target_properties("${target_name}" PROPERTIES OUTPUT_NAME "${output_name}") set_property(GLOBAL APPEND PROPERTY SFT_BINARIES "${target_name}") # process further arguments foreach(flag ${ARGN}) if(flag STREQUAL "allow_no_undefined") if(NOT type STREQUAL "library") message(FATAL_ERROR "finalize_binary flag 'allow_no_undefined' is only valid for libraries!") endif() if(NOT "${CMAKE_CXX_FLAGS}" MATCHES "-fsanitize" AND NOT MSVC) if(APPLE) set(link_flag "-undefined,error") else() set(link_flag "--no-undefined") endif() set_target_properties("${target_name}" PROPERTIES COMPILE_FLAGS "${EXTRA_FLAGS}" LINK_FLAGS "-Wl,${link_flag}" ) endif() else() message(FATAL_ERROR "declare_binary encountered unknown flag: ${flag}") endif() endforeach() endfunction() # add source files to a binary # # by default, the filename is interpreted as relative to the current directory # to specify absolute filenames, write ABSOLUTE # to specify a file that will be auto-generated, write GENERATED, all files after # this modifier are marked as generated. function(add_sources target_name) set(generated FALSE) get_property(binary_list GLOBAL PROPERTY SFT_BINARIES) list(FIND binary_list "${target_name}" index) if(index EQUAL -1) message(FATAL_ERROR "attempting to add source to unknown binary ${target_name}") endif() foreach(source ${ARGN}) if(source STREQUAL GENERATED) set(generated TRUE) else() if(NOT IS_ABSOLUTE "${source}") set(source "${CMAKE_CURRENT_SOURCE_DIR}/${source}") endif() file(TO_CMAKE_PATH "${source}" source) # add all sources as private, otherwise _ALL SOURCES_ # would be compiled again for each library that links against # the $target_name target_sources("${target_name}" PRIVATE "${source}") if(generated) set_source_files_properties("${source}" PROPERTIES GENERATED ON) set_property(GLOBAL PROPERTY SFT_BINARY_HAS_GENERATED_SRCS_${target_name} TRUE) endif() endif() endforeach() endfunction() # finalize the executable definition function(finalize_binary target_name) get_property(has_generated_sources GLOBAL PROPERTY SFT_BINARY_HAS_GENERATED_SRCS_${target_name}) # make the binary depend on codegen iff it has any generated files #if(has_generated_sources) # add_dependencies("${target_name}" codegen) #endif() pretty_print_target("cpp" "${target_name}" "[${source_file_count} sources]") endfunction() ================================================ FILE: buildsystem/cythonize.py ================================================ #!/usr/bin/env python3 # # Copyright 2015-2025 the openage authors. See copying.md for legal info. """ Runs Cython on all modules that were listed via add_cython_module. """ import argparse import os import sys from contextlib import redirect_stdout from multiprocessing import cpu_count from pathlib import Path from Cython.Build import cythonize class LineFilter: """ Proxy for a stream (default stdout) to filter out whole unwanted lines """ # pylint: disable=too-few-public-methods def __init__(self, stream=None, filters=None): self.stream = stream or sys.stdout self.filters = filters or [] self.buf = "" def __getattr__(self, attr_name): return getattr(self.stream, attr_name) def __enter__(self): return self def __exit__(self, *args): self.stream.write(self.buf) self.buf = "" def write(self, data): """ Writes to output stream, buffered line-wise, omitting lines given in to constructor in filter """ self.buf += data lines = self.buf.split('\n') for line in lines[:-1]: if not any(f(line) for f in self.filters): self.stream.write(line + '\n') self.buf = lines[-1] class CythonFilter(LineFilter): """ Filters output of cythonize for useless warnings """ # pylint: disable=too-few-public-methods def __init__(self): filters = [ lambda x: 'put "# distutils: language=c++" in your .pyx or .pxd file(s)' in x, lambda x: x.startswith('Compiling ') and x.endswith(' because it changed.') ] super().__init__(filters=filters) def read_list_from_file(filename): """ Reads a semicolon-separated list of file entires """ with open(filename, encoding='utf8') as fileobj: data = fileobj.read().strip() return [Path(filename).resolve() for filename in data.split(';')] def remove_if_exists(filename): """ Deletes the file (if it exists) """ if filename.is_file(): print(filename.relative_to(os.getcwd())) filename.unlink() def cythonize_wrapper(modules, force_optimized_lib = False, **kwargs): """ Calls cythonize, filtering useless warnings """ bin_dir, bin_modules = kwargs['build_dir'], [] src_dir, src_modules = Path.cwd(), [] for module in modules: if Path(bin_dir) in module.parents: bin_modules.append(str(module.relative_to(bin_dir))) else: src_modules.append(str(module.relative_to(src_dir))) with CythonFilter() as cython_filter: with redirect_stdout(cython_filter): if src_modules: cythonize(src_modules, **kwargs) if sys.platform == 'win32' and force_optimized_lib: win_use_optimized_lib_python(src_modules, bin_dir) if bin_modules: os.chdir(bin_dir) cythonize(bin_modules, **kwargs) if sys.platform == 'win32' and force_optimized_lib: win_use_optimized_lib_python(bin_modules, bin_dir) os.chdir(src_dir) def win_use_optimized_lib_python(modules, path): """ Add an #ifdef statement in cythonized .cpp files to temporarily undefine _DEBUG before #include "Python.h" This function modifies the generated C++ files from Cython to prevent linking to the debug version of the Python library on Windows. The debug version of the Python library cannot import Python libraries that contain extension modules. see: https://github.com/python/cpython/issues/127619 (To unserstand the problem) see: https://stackoverflow.com/a/59566420 (To understand the soloution) """ for module in modules: module = str(module) if path: module = path + "\\" + module module = module.removesuffix(".py").removesuffix(".pyx") module = module + ".cpp" with open(module, "r", encoding='utf8') as file: text = file.read() if not text.count("OPENAGE: UNDEF_DEBUG_INSERTED"): text = text.replace( '#include "Python.h"', ( "\n\n// OPENAGE: UNDEF_DEBUG_INSERTED\n" "// Avoid linking to the debug version of the Python library on Windows\n\n" "#ifdef _DEBUG\n" "#define _DEBUG_WAS_DEFINED\n" "#undef _DEBUG\n#endif\n" "#include \"Python.h\"\n" "#ifdef _DEBUG_WAS_DEFINED\n" "#define _DEBUG\n" "#undef _DEBUG_WAS_DEFINED\n" "#endif\n\n" "// OPENAGE: UNDEF_DEBUG_INSERTED\n\n" ), 1 ) with open(module, "w", encoding='utf8') as file: file.write(text) def main(): """ CLI entry point """ cli = argparse.ArgumentParser() cli.add_argument("module_list", help=( "Module list file (semicolon-separated)." )) cli.add_argument("embedded_module_list", help=( "Embedded module list file (semicolon-separated).\n" "Modules in this list are compiled with the --embed option." )) cli.add_argument("depends_list", help=( "Dependency list file (semicolon-separated).\n" "Contains all .pxd and other files that may get included.\n" "Used to verify that all dependencies are properly listed " "in the CMake build configuration." )) cli.add_argument("--clean", action="store_true", help=( "Clean compilation results and exit." )) cli.add_argument("--build-dir", help=( "Build output directory to generate the cpp files in." "note: this is also added for module search path." )) cli.add_argument("--memcleanup", type=int, default=0, help=( "Generate memory cleanup code to make valgrind happy:\n" "0: nothing, 1+: interned objects,\n" "2+: cdef globals, 3+: types objects" )) cli.add_argument("--threads", type=int, default=cpu_count(), help="number of compilation threads to use") cli.add_argument("--force_optimized_lib", action="store_true", help= "edit compiled .cpp files to link to optimized version of python libary") args = cli.parse_args() # cython emits warnings on using absolute paths to modules # https://github.com/cython/cython/issues/2323 modules = read_list_from_file(args.module_list) embedded_modules = read_list_from_file(args.embedded_module_list) depends = set(read_list_from_file(args.depends_list)) if args.clean: for module in modules + embedded_modules: rel_module = module.relative_to(Path.cwd()) build_module = args.build_dir / rel_module remove_if_exists(build_module.with_suffix('.cpp')) remove_if_exists(build_module.with_suffix('.html')) sys.exit(0) from Cython.Compiler import Options Options.annotate = True Options.fast_fail = True Options.generate_cleanup_code = args.memcleanup Options.cplus = 1 # build cython modules (emits shared libraries) cythonize_args = { 'compiler_directives': {'language_level': 3}, 'build_dir': args.build_dir, 'include_path': [args.build_dir], 'nthreads': args.threads } # this is deprecated, but still better than # writing funny lines at the head of each file. cythonize_args['language'] = 'c++' cythonize_wrapper(modules, args.force_optimized_lib, **cythonize_args) # build standalone executables that embed the py interpreter Options.embed = "main" cythonize_wrapper(embedded_modules, args.force_optimized_lib, **cythonize_args) # verify depends from Cython.Build.Dependencies import _dep_tree depend_failed = False # TODO figure out a less hacky way of getting the depends out of Cython # pylint: disable=no-member, protected-access for module, files in _dep_tree.__cimported_files_cache.items(): for filename in files: if not filename.startswith('.'): # system include starts with / continue if os.path.realpath(os.path.abspath(filename)) not in depends: print("\x1b[31mERR\x1b[m unlisted dependency: " + filename) depend_failed = True if depend_failed: sys.exit(1) if __name__ == '__main__': main() ================================================ FILE: buildsystem/doxygen.cmake ================================================ # Copyright 2014-2016 the openage authors. See copying.md for legal info. # Doxygen integration # enable doxygen for all given folder names function(doxygen_init) find_package(Doxygen) if(DOXYGEN_FOUND) find_file(DOT dot HINTS "/usr/bin/dot") if(NOT ${DOT} STREQUAL "DOT-NOTFOUND") set(DOT_EXECUTABLE "${DOT}" PARENT_SCOPE) set(HAVE_DOT "YES" PARENT_SCOPE) else() set(DOT_EXECUTABLE "/bin/false" PARENT_SCOPE) set(HAVE_DOT "NO" PARENT_SCOPE) message(WARNING "graphviz dot couldn't be found, you won't have cool graphs in the docs.") endif() endif() set(DOXYGEN_EXECUTABLE "${DOXYGEN_EXECUTABLE}" PARENT_SCOPE) set(DOXYGEN_FOUND "${DOXYGEN_FOUND}" PARENT_SCOPE) endfunction() function(doxygen_configure) if(DOXYGEN_FOUND) # add doc target add_custom_target(doc "${DOXYGEN_EXECUTABLE}" "${CMAKE_BINARY_DIR}/Doxyfile" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "generating docs (via Doxygen)" VERBATIM ) # create doc folder name list foreach(folder ${ARGN}) set(DOXYGEN_SCAN_FOLDERS "${DOXYGEN_SCAN_FOLDERS} \"${CMAKE_CURRENT_SOURCE_DIR}/${folder}\"") endforeach() # adapt doxygen config configure_file("${BUILDSYSTEM_DIR}/templates/Doxyfile.in" "${CMAKE_BINARY_DIR}/Doxyfile" @ONLY) else() # add no-op doc target add_custom_target(doc COMMAND echo "When you configured the project, Doxygen could not be found." COMMAND echo "Install Doxygen and `./configure` again, then try to generate docs." COMMAND false VERBATIM ) message(WARNING "doxygen couldn't be found, you won't be able to generate docs") endif() endfunction() doxygen_init() ================================================ FILE: buildsystem/doxygen_custom.css ================================================ /******************************************************* * Make footer black and content white */ body { background-color: #445; } div.contents { background-color: white; padding-top: 10px; padding-left: 12px; padding-right: 8px; padding-bottom: 1em; margin: 0; } /******************************************************* * Header */ #titlearea { padding: 1em 0; background-color: white; border-bottom: 2px solid #5373B4; } #projectlogo { padding-left: 1em; text-align: left; } #projectlogo img { height: 6em; } #projectname { visibility: hidden; /* Hide project name, it's already on the logo */ text-align: right; padding-right: .3em; } #projectnumber { visibility: visible; /* Show openage version, because this is a children of #projectname and I've made that invisible */ } #projectalign { width: 100%; /* So I can align the version number to the right */ } /******************************************************* * Main navbar */ .sm { background-image: none; background-color: black; } .sm a { background-image: none; background-color: black; color: white; text-shadow: 0 1px 1px black; border: none; } .sm a.highlighted, .sm a:hover, .sm a:active, .sm a:focus { background-image: url("tab_a.png"); background-repeat: repeat; text-shadow: 0 1px 1px black; border: none; } .sm ul a.highlighted, .sm ul a:hover, .sm ul a:active, .sm ul a:focus { background-image: none; background-color: #ccc; text-shadow: none; border: none; color: inherit; } /* * Those tiny arrows are instead a 0px box with a 4px border, that triangle it's * the one border, the remaining borders are transparent */ .sm a span.sub-arrow { border-color: white transparent transparent transparent; } .sm ul a span.sub-arrow { border-color: transparent transparent transparent black; } .sm ul a.highlighted span.sub-arrow { border-color: transparent transparent transparent white; } /******************************************************* * Hierarchy navbar */ .navpath ul { background-color: #445; background-image: none; border: 0; } .navpath ul li.navelem { color: white; } .navpath ul li.navelem a { color: #eee; text-shadow: 0px 1px 1px black; } .navpath ul li.navelem a:hover { color: white; text-decoration: underline; } /******************************************************* * Directory tables */ table.directory tr.even { background-color: #eee; } table.directory td.entry { color: #444; } table.directory td.desc { border-left: 2px solid white; } /* Icons looked off */ .iconfclosed, .iconfopen, .icondoc { margin-top: 3px; } /******************************************************* * Footer */ hr.footer { display: none; } address.footer { /*background-color: black;*/ color: white; padding: 1em; border-top: 2px solid #aac; } div.contents { background-color: white; padding-top: 10px; padding-left: 12px; padding-right: 8px; padding-bottom: 1em; margin: 0; } /******************************************************* * Content */ div.textblock { max-width: 900px; } /* * Single-line code blocks */ pre.fragment { padding: .7em; border: none; background-color: #f2f2f5; } /* * Multiple-line code blocks */ div.fragment { border: none; background-color: #f2f2f5; padding: .7em; } div.fragment div.line { padding: .5em; text-indent: 0; } div.fragment div.line span.lineno { padding: .5em; text-indent: 0; border-right: 2px solid #aac; } /* * Inline code */ code { background-color: #eee; padding: 0.3em; margin: 0; font-size: 85%; border-radius: 3px; } /* * Tables */ table.doxtable { border-bottom: 1px solid #9CAFD4; } table.doxtable th { background-color: #374F7F; color: white; border: none; } table.doxtable tr:nth-child(even) { background-color: #f2f2f5; } table.doxtable tr:nth-child(odd) { background-color: white; } table.doxtable td { border: none; } /* Index letters (those in a black box) */ div.ah, span.ah { background-color: black; background-image: none; border: none; } /* Index bar */ div.qindex { border: 1px solid #bac8e4; border-left: 0; border-right: 0; padding: .7em 0; background-color: white; } ================================================ FILE: buildsystem/inplacemodules.py ================================================ # Copyright 2015-2021 the openage authors. See copying.md for legal info. """ Installs the Python extension modules that were created in the configuration specific subdirectory of the build directory to the places where they would be expected. Attemts to use OS facilities such as hardlinking, and falls back to copying. """ import argparse import os from pathlib import Path import shutil def main(): """ CLI entry point """ cli = argparse.ArgumentParser() cli.add_argument("module_list_file", help=( "semicolon-separated list of all modules that shall be installed " "in-place." )) cli.add_argument("configuration", help=( "the build configuration like Debug or Release" )) cli.add_argument("--clean", action="store_true", help=( "remove instead of creating" )) args = cli.parse_args() with open(args.module_list_file, encoding='utf8') as fileobj: modules = fileobj.read().strip().split(';') if modules == ['']: modules = [] for module in modules: sourcefile = Path(module) if args.configuration in sourcefile.parts: # If `sourcefile` has a configuration component, remove it. file_parts = list(sourcefile.parts) file_parts.remove(args.configuration) targetfile = Path(*file_parts) else: continue if targetfile.exists(): if args.clean: print(targetfile) targetfile.unlink() continue if targetfile.stat().st_mtime >= sourcefile.stat().st_mtime: continue targetfile.unlink() if args.clean: continue # try to hardlink try: os.link(sourcefile, targetfile) continue except OSError: pass # we don't try to symlink, because then Python may "intelligently" # determine the actual location, and refuse to work. # fallback to copying the file shutil.copy(sourcefile, targetfile) if __name__ == '__main__': main() ================================================ FILE: buildsystem/modules/DownloadCache.cmake ================================================ # Copyright 2017-2017 the openage authors. See copying.md for legal info. # DownloadCache, as the name suggests, downloads a file and # if it succeeds, caches it for subsequent runs. # function args: # * url: the source URL to download the file from. # * download_path: the destination file path. function(download_cache url download_path) set(download_stamp_path "${download_path}.stamp") if(EXISTS "${download_stamp_path}") message(STATUS "using cached ${download_path}") return() endif() file(DOWNLOAD "${url}" "${download_path}" STATUS download_status SHOW_PROGRESS ) list(GET download_status 0 status_code) if(NOT status_code EQUAL 0) list(GET dowload_status 1 reason) message(FATAL_ERROR "Failed to download from ${url}: ${reason}") endif() file(WRITE "${download_stamp_path}" "") endfunction() ================================================ FILE: buildsystem/modules/FindCython.cmake ================================================ # Copyright 2015-2016 the openage authors. See copying.md for legal info. # This module defines: # # CYTHON_FOUND # CYTHON_VERSION - e.g. '0.23dev' # CYTHON - invocation (using python) # # It depends on: # # PYTHON set(CYTHON "${PYTHON} -m cython") py_exec("import cython; print(cython.__version__)" CYTHON_VERSION) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Cython VERSION_VAR CYTHON_VERSION REQUIRED_VARS CYTHON) ================================================ FILE: buildsystem/modules/FindEpoxy.cmake ================================================ # Copyright 2015-2016 the openage authors. See copying.md for legal info. find_path(EPOXY_INCLUDE_DIRS epoxy/gl.h HINTS $ENV{EPOXY_DIR} PATH_SUFFIXES include src PATHS /usr/include /usr/local/include /sw/include /opt/local/include /usr/freeware/include ) find_library(EPOXY_LIBRARIES NAMES epoxy HINTS $ENV{EPOXY_DIR} PATH_SUFFIXES lib64 lib PATHS /usr/lib /usr/local/lib /sw /usr/freeware ) include("FindPackageHandleStandardArgs") find_package_handle_standard_args(Epoxy DEFAULT_MSG EPOXY_LIBRARIES EPOXY_INCLUDE_DIRS) mark_as_advanced(EPOXY_LIBRARIES EPOXY_INCLUDE_DIRS) ================================================ FILE: buildsystem/modules/FindGCCBacktrace.cmake ================================================ # Copyright 2015-2017 the openage authors. See copying.md for legal info. # This module defines: # # GCCBacktrace_FOUND - whether libbacktrace was found # GCCBacktrace_INCLUDE_DIRS - The include dirs for libbacktrace # GCCBacktrace_LIBRARIES - The list of libraries find_path(GCCBacktrace_INCLUDE_DIR "backtrace.h") find_library(GCCBacktrace_LIBRARY NAMES "backtrace" PATHS /usr/lib /usr/local/lib /sw /usr/freeware ) include(CheckCXXSourceCompiles) list(APPEND CMAKE_REQUIRED_INCLUDES ${GCCBacktrace_INCLUDE_DIR}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${GCCBacktrace_LIBRARY}) set(CMAKE_REQUIRED_QUIET TRUE) CHECK_CXX_SOURCE_COMPILES(" #include #include int callback(void * /* unused */, uintptr_t pc) { std::cout << (void *) pc << std::endl; return 0; } int main() { struct backtrace_state *state = nullptr; state = backtrace_create_state(nullptr, false, nullptr, nullptr); backtrace_simple(state, 0, callback, nullptr, nullptr); return 0; } " HAVE_GCC_BACKTRACE) if(NOT HAVE_GCC_BACKTRACE) unset(GCCBacktrace_INCLUDE_DIR) unset(GCCBacktrace_LIBRARY) else() set(GCCBacktrace_LIBRARIES ${GCCBacktrace_LIBRARY}) set(GCCBacktrace_INCLUDE_DIRS ${GCCBacktrace_INCLUDE_DIR}) endif() # handle the QUIETLY and REQUIRED arguments and set BACKTRACE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args( GCCBacktrace FOUND_VAR GCCBacktrace_FOUND REQUIRED_VARS GCCBacktrace_LIBRARIES GCCBacktrace_INCLUDE_DIRS) mark_as_advanced(GCCBacktrace_LIBRARIES GCCBacktrace_INCLUDE_DIRS) ================================================ FILE: buildsystem/modules/FindGPerfTools.cmake ================================================ # This file was taken from VAST, # Copyright 2014-2014 Matthias Vallentin. # It's licensed under the terms of the 3-clause BSD license. # Modifications Copyright 2014-2020 the openage authors. # See copying.md for further legal info. # Tries to find Gperftools. # # Usage of this module as follows: # # find_package(Gperftools) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # Gperftools_ROOT_DIR Set this variable to the root installation of # Gperftools if the module has problems finding # the proper installation path. # # Variables defined by this module: # # GPERFTOOLS_FOUND System has Gperftools libs/headers # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler) # GPERFTOOLS_TCMALLOC The tcmalloc library # GPERFTOOLS_PROFILER The profiler library # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers find_library(GPERFTOOLS_TCMALLOC NAMES tcmalloc HINTS ${Gperftools_ROOT_DIR}/lib) find_library(GPERFTOOLS_PROFILER NAMES profiler HINTS ${Gperftools_ROOT_DIR}/lib) find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER NAMES tcmalloc_and_profiler HINTS ${Gperftools_ROOT_DIR}/lib) find_path(GPERFTOOLS_INCLUDE_DIR NAMES gperftools/heap-profiler.h HINTS ${Gperftools_ROOT_DIR}/include) set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( GPerfTools DEFAULT_MSG GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR) mark_as_advanced( Gperftools_ROOT_DIR GPERFTOOLS_TCMALLOC GPERFTOOLS_PROFILER GPERFTOOLS_TCMALLOC_AND_PROFILER GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR) ================================================ FILE: buildsystem/modules/FindHarfBuzz.cmake ================================================ # Copyright 2015-2016 the openage authors. See copying.md for legal info. # FindHarfBuzz # --------- # # Locate HarfBuzz, the awesome text shaping library. # # The module defines the following variables: # # :: # # HarfBuzz_FOUND - Found HarfBuzz library # HarfBuzz_INCLUDE_DIRS - HarfBuzz include directories # HarfBuzz_LIBRARIES - The libraries needed to use HarfBuzz # HarfBuzz_VERSION_STRING - the version of HarfBuzz found # # Example Usage: # # :: # # find_package(HarfBuzz REQUIRED) # include_directories(${HarfBuzz_INCLUDE_DIRS}) # # :: # # add_executable(foo foo.cc) # target_link_libraries(foo ${HarfBuzz_LIBRARIES}) find_path(HarfBuzz_INCLUDE_DIR harfbuzz/hb.h HINTS $ENV{HARFBUZZ_DIR} PATHS /usr/include /usr/local/include /sw/include /opt/local/include /usr/freeware/include ) find_library(HarfBuzz_LIBRARY NAMES harfbuzz libharfbuzz HINTS $ENV{HARFBUZZ_DIR} PATH_SUFFIXES lib64 lib PATHS /usr/lib /usr/local/lib /sw /usr/freeware ) if(HarfBuzz_INCLUDE_DIR) set(HarfBuzz_VERSION_FILE "${HarfBuzz_INCLUDE_DIR}/harfbuzz/hb-version.h") if(EXISTS "${HarfBuzz_VERSION_FILE}") file(STRINGS "${HarfBuzz_VERSION_FILE}" hb_version_str REGEX "^#define[\t ]+HB_VERSION_STRING[\t ]+\".*\"") string(REGEX REPLACE "^#define[\t ]+HB_VERSION_STRING[\t ]+\"([^\"]*)\".*" "\\1" HarfBuzz_VERSION_STRING "${hb_version_str}") unset(hb_version_str) endif() unset(HarfBuzz_VERSION_FILE) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(HarfBuzz FOUND_VAR HarfBuzz_FOUND REQUIRED_VARS HarfBuzz_LIBRARY HarfBuzz_INCLUDE_DIR VERSION_VAR HarfBuzz_VERSION_STRING ) if(HarfBuzz_FOUND) set(HarfBuzz_INCLUDE_DIRS "${HarfBuzz_INCLUDE_DIR}") set(HarfBuzz_LIBRARIES "${HarfBuzz_LIBRARY}") endif() mark_as_advanced(HarfBuzz_INCLUDE_DIR HarfBuzz_LIBRARY) ================================================ FILE: buildsystem/modules/FindInotify.cmake ================================================ # Copyright 2014-2020 the openage authors. See copying.md for legal info. # This module defines # # INOTIFY_INCLUDE_DIR # INOTIFY_FOUND find_path(INOTIFY_INCLUDE_DIR sys/inotify.h HINTS /usr/include/${CMAKE_LIBRARY_ARCHITECTURE}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Inotify DEFAULT_MSG INOTIFY_INCLUDE_DIR) mark_as_advanced(INOTIFY_INCLUDE_DIR) ================================================ FILE: buildsystem/modules/FindOgg.cmake ================================================ # Copyright 2018-2018 the openage authors. See copying.md for legal info. # - Find ogg library # Find the native Ogg headers and library. # This module defines # OGG_INCLUDE_DIRS - where to find ogg/ogg.h etc. # OGG_LIBRARIES - List of libraries when using libogg # OGG_FOUND - True if ogg is found. find_path(OGG_INCLUDE_DIR NAMES ogg/ogg.h DOC "Ogg include directory" ) find_library(OGG_LIBRARY NAMES ogg DOC "Path to ogg library" ) # handle the QUIETLY and REQUIRED arguments and set OGG_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Ogg DEFAULT_MSG OGG_LIBRARY OGG_INCLUDE_DIR) mark_as_advanced(OGG_INCLUDE_DIR OGG_LIBRARY) # export the variables set(OGG_LIBRARIES "${OGG_LIBRARY}") set(OGG_INCLUDE_DIRS "${OGG_INCLUDE_DIR}") ================================================ FILE: buildsystem/modules/FindOpusfile.cmake ================================================ # This file was taken from Unvanquished, # Copyright 2000-2009 Kitware, Inc., Insight Software Consortium # It's licensed under the terms of the 3-clause OpenBSD license. # Modifications Copyright 2014-2020 the openage authors. # See copying.md for further legal info. # - Find opus library # Find the native Opus headers and libraries. # This module defines # OPUS_INCLUDE_DIRS - where to find opus/opus.h, opus/opusfile.h, etc # OPUS_LIBRARIES - List of libraries when using libopus # OPUSFILE_FOUND - True if opus is found. # find the opusfile header, defines our api. find_path(OPUSFILE_INCLUDE_DIR NAMES opus/opusfile.h DOC "Opusfile include directory" ) mark_as_advanced(OPUSFILE_INCLUDE_DIR) # find the opus header find_path(OPUS_INCLUDE_DIR NAMES opus/opus.h DOC "Opus include directory" ) mark_as_advanced(OPUS_INCLUDE_DIR) # look for libopusfile, the highlevel container-aware api. find_library(OPUSFILE_LIBRARY NAMES opusfile DOC "Path to OpusFile library" ) mark_as_advanced(OPUSFILE_LIBRARY) # find libopus, the core codec component. find_library(OPUS_LIBRARY NAMES opus DOC "Path to Opus library" ) mark_as_advanced(OPUS_LIBRARY) # handle the QUIETLY and REQUIRED arguments and set OPUS_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Opusfile DEFAULT_MSG OPUSFILE_LIBRARY OPUS_LIBRARY OPUS_INCLUDE_DIR) # export the variables set(OPUS_LIBRARIES "${OPUSFILE_LIBRARY}" "${OPUS_LIBRARY}") set(OPUS_INCLUDE_DIRS "${OPUSFILE_INCLUDE_DIR}" "${OPUS_INCLUDE_DIR}" "${OPUS_INCLUDE_DIR}/opus") ================================================ FILE: buildsystem/modules/FindPython.cmake ================================================ # Copyright 2015-2025 the openage authors. See copying.md for legal info. # Find Python # ~~~~~~~~~~~ # # Find the Python interpreter, and related info. # # This is a wrapper around FindPython3.cmake, # which sets many more variables: # https://cmake.org/cmake/help/latest/module/FindPython3.html # # This file defines the following variables: # # PYTHON_FOUND - True when python was found. # PYTHON - The full path to the Python interpreter. # PYTHON_INCLUDE_DIRS - Include path for Python extensions. # PYTHON_LIBRARIES - Library and Linker options for Python extensions. # # Also defines py_exec and py_get_config_var. ############################################################### set(PYTHON_USED_VERSION "${PYTHON_MIN_VERSION}") # You can manually pass the directory to an interpreter # by defining PYTHON_DIR or passing -DPYTHON_DIR="" # to CMake. It's used as a hint where to look at # https://cmake.org/cmake/help/latest/module/FindPython3.html#hints if(PYTHON_DIR) set(Python3_ROOT_DIR "${PYTHON_DIR}") elseif(Python3_ROOT_DIR OR Python3_EXECUTABLE) # python paths given directly else() # when there are multiple pythons, preferrably use the version of # the default `python3` executable. execute_process( COMMAND "python3" -c "import platform; print(platform.python_version())" OUTPUT_VARIABLE PYVER_OUTPUT RESULT_VARIABLE PYVER_RETVAL ) # if a system version exists, check if it's compatible with our min requirements if(PYVER_RETVAL EQUAL 0) string(REGEX MATCH "^[0-9]+\\.[0-9]+" PYTHON_USED_VERSION "${PYVER_OUTPUT}") if(PYTHON_USED_VERSION VERSION_GREATER_EQUAL PYTHON_MIN_VERSION) # set EXACT so we get the system version from find_package set(need_exact_version "EXACT") else() # search for alternatives if version doesn't fulfill min requirements set(PYTHON_USED_VERSION "${PYTHON_MIN_VERSION}") endif() endif() endif() ############################################################### # Never use the Windows Registry to find python set(Python3_FIND_REGISTRY "NEVER") # use cmake's FindPython3 to locate library and interpreter find_package(Python3 ${PYTHON_USED_VERSION} ${need_exact_version} COMPONENTS Interpreter Development NumPy) # python version string to cpython api test in modules/FindPython_test.cpp # we use the solution from CPython's header (see https://github.com/SFTtech/openage/issues/1438#issuecomment-1036311012): # define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \ # (PY_MINOR_VERSION << 16) | \ # (PY_MICRO_VERSION << 8) | \ # (PY_RELEASE_LEVEL << 4) | \ # (PY_RELEASE_SERIAL << 0)) math(EXPR BIT_SHIFT_HEX "${Python3_VERSION_MAJOR} << 24 | ${Python3_VERSION_MINOR} << 16" OUTPUT_FORMAT HEXADECIMAL) set(PYTHON_MIN_VERSION_HEX "${BIT_SHIFT_HEX}") # there's a static_assert that tests the Python version. # that way, we verify the interpreter and the library version. # (the interpreter provided us the library location) if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(TEMP_CMAKE_TRY_COMPILE_CONFIGURATION ${CMAKE_TRY_COMPILE_CONFIGURATION}) set(CMAKE_TRY_COMPILE_CONFIGURATION "Release") endif() try_compile(PYTHON_TEST_RESULT "${CMAKE_BINARY_DIR}" SOURCES "${CMAKE_CURRENT_LIST_DIR}/FindPython_test.cpp" LINK_LIBRARIES Python3::Python CXX_STANDARD ${CMAKE_CXX_STANDARD} COMPILE_DEFINITIONS "-DTARGET_VERSION=${PYTHON_MIN_VERSION_HEX}" OUTPUT_VARIABLE PYTHON_TEST_OUTPUT ) if(WIN32 AND "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") set(CMAKE_TRY_COMPILE_CONFIGURATION ${TEMP_CMAKE_TRY_COMPILE_CONFIGURATION}) endif() if(NOT PYTHON_TEST_RESULT) message(STATUS "!! No suitable Python interpreter was found !! We need a Python interpreter >= ${PYTHON_MIN_VERSION} that is shipped with libpython and header files. You can tell the python find module where your python is installed: Executable location: -DPython3_EXECUTABLE=/path/to/executable Root directory: -DPython3_ROOT_DIR=/directory/of/executable/ We tried with Python ${PYTHON_FIND_VERSION}, but test compilation failed: ${PYTHON_TEST_OUTPUT} ") message(FATAL_ERROR "No suitable Python was found!") elseif(PYTHON_TEST_RESULT) # Interfacing # Python.cmake vars <= Python3.cmake vars set(PYTHON ${Python3_EXECUTABLE} CACHE FILEPATH "Location of the Python interpreter" FORCE) set(PYTHON_FOUND ${Python3_Interpreter_FOUND}) set(PYTHON_LIBRARIES ${Python3_LIBRARIES} CACHE STRING "Linker invocation for the Python library" FORCE) set(PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS} CACHE PATH "Location of the Python include dir" FORCE) set(PYTHON_VERSION_STRING ${Python3_VERSION}) # Numpy.cmake vars <= Python3.cmake vars set(NUMPY_FOUND ${Python3_NumPy_FOUND}) set(NUMPY_VERSION ${Python3_NumPy_VERSION}) set(NUMPY_INCLUDE_DIR ${Python3_NumPy_INCLUDE_DIRS} CACHE STRING "Linker invocation for the NumPy library" FORCE) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Python REQUIRED_VARS PYTHON PYTHON_INCLUDE_DIRS PYTHON_LIBRARIES) endif() unset(PYTHON_TEST_RESULT) unset(PYTHON_TEST_OUTPUT) # helper functions function(py_exec STATEMENTS RESULTVAR) # executes some python statement(s), and returns the result in RESULTVAR. # aborts with a fatal error on error. # no single quotes are allowed in STATEMENTS. execute_process( COMMAND "${PYTHON}" -c "${STATEMENTS}" OUTPUT_VARIABLE PY_OUTPUT RESULT_VARIABLE PY_RETVAL ) if(NOT PY_RETVAL EQUAL 0) message(FATAL_ERROR "failed:\n${PYTHON} -c '${STATEMENTS}'\n${PY_OUTPUT}") endif() string(STRIP "${PY_OUTPUT}" PY_OUTPUT_STRIPPED) set("${RESULTVAR}" "${PY_OUTPUT_STRIPPED}" PARENT_SCOPE) endfunction() function(py_get_config_var VAR RESULTVAR) # uses py_exec to determine a config var as in sysconfig.get_config_var(). py_exec( "from sysconfig import get_config_var; print(get_config_var('${VAR}'))" RESULT ) set("${RESULTVAR}" "${RESULT}" PARENT_SCOPE) endfunction() ================================================ FILE: buildsystem/modules/FindPython_test.cpp ================================================ // Copyright 2014-2023 the openage authors. See copying.md for legal info. #include int main() { // set by preprocessor: constexpr uint64_t min_ver = TARGET_VERSION; static_assert(PY_VERSION_HEX >= min_ver, "unsuitable: " PY_VERSION); Py_Initialize(); Py_Finalize(); } ================================================ FILE: buildsystem/options.cmake ================================================ # Copyright 2014-2018 the openage authors. See copying.md for legal info. # logs whether the option NAME is enabled # sets WITH_${VARNAME} to HAVE # errors if WANT_${VARNAME} conflicts with HAVE function(have_config_option NAME VARNAME HAVE) set(WANT "${WANT_${VARNAME}}") set(WITH_${VARNAME} "${HAVE}" PARENT_SCOPE) if(HAVE) set_property(GLOBAL APPEND PROPERTY SFT_CONFIG_OPTIONS_ENABLED "${NAME}") if(NOT WANT) message(FATAL_ERROR "${NAME}: WANT_${VARNAME}=${WANT}, but WITH_${VARNAME}=${HAVE}") endif() else() set_property(GLOBAL APPEND PROPERTY SFT_CONFIG_OPTIONS_DISABLED "${NAME}") if(WANT STREQUAL "if_available") message(STATUS "optional dependency is unavailable: ${NAME}") elseif(WANT) message(FATAL_ERROR "${NAME}: WANT_${VARNAME}=${WANT}, but WITH_${VARNAME}=${HAVE}") endif() endif() endfunction() function(print_config_options) get_property(enabled_opts GLOBAL PROPERTY SFT_CONFIG_OPTIONS_ENABLED) get_property(disabled_opts GLOBAL PROPERTY SFT_CONFIG_OPTIONS_DISABLED) message("enabled options:") if(enabled_opts) foreach(opt ${enabled_opts}) message("\t${opt}") endforeach() else() message("\t") endif() message("") message("disabled options:") if(disabled_opts) foreach(opt ${disabled_opts}) message("\t${opt}") endforeach() else() message("\t") endif() message("") endfunction() function(get_config_option_string) get_property(enabled_opts GLOBAL PROPERTY SFT_CONFIG_OPTIONS_ENABLED) if(enabled_opts) LIST(GET enabled_opts 0 CONFIG_OPTION_STRING) LIST(REMOVE_AT enabled_opts 0) foreach(opt ${enabled_opts}) set(CONFIG_OPTION_STRING "${CONFIG_OPTION_STRING}, ${opt}") endforeach() else() set(CONFIG_OPTION_STRING "< no options enabled >") endif() set(CONFIG_OPTION_STRING "${CONFIG_OPTION_STRING}" PARENT_SCOPE) endfunction() ================================================ FILE: buildsystem/pxdgen.py ================================================ # Copyright 2015-2023 the openage authors. See copying.md for legal info. """ Auto-generates PXD files from annotated C++ headers. Invoked via cmake during the regular build process. """ import argparse import os from pathlib import Path import re import sys from pygments.token import Token from pygments.lexers import get_lexer_for_filename CWD = os.getcwd() class ParserError(Exception): """ Represents a fatal parsing error in PXDGenerator. """ def __init__(self, filename, lineno, message): super().__init__(f"{filename}:{lineno} {message}") class PXDGenerator: """ Represents, and performs, a single conversion of a C++ header file to a PXD file. @param filename: input (C++ header) file name. is opened and read. the output filename is the same, but with .pxd instead of .h. """ def __init__(self, filename): self.filename = filename self.warnings = [] # current parsing state (not valid until self.parse() is called) self.stack, self.lineno, self.annotations = None, None, None def parser_error(self, message, lineno=None): """ Returns a ParserError object for this generator, at the current line. """ if lineno is None: lineno = self.lineno return ParserError(self.filename, lineno, message) def tokenize(self): """ Tokenizes the input file. Yields (tokentype, val) pairs, where val is a string. The concatenation of all val strings is equal to the input file's content. """ # contains all namespaces and other '{' tokens self.stack = [] # current line number self.lineno = 1 # we're using the pygments lexer (mainly because that was the first # google hit for 'python c++ lexer', and it's fairly awesome to use) lexer = get_lexer_for_filename('.cpp') with open(self.filename, encoding='utf8') as infile: code = infile.read() for token, val in lexer.get_tokens(code): # ignore whitespaces yield token, val self.lineno += val.count('\n') def handle_singleline_comment(self, val): """ Breaks down a '//'-style single-line comment, and passes the result to handle_comment() @param val: the comment text, as string, including the '//' """ try: val = re.match('^// (.*)$', val).group(1) except AttributeError as ex: raise self.parser_error("invalid single-line comment") from ex self.handle_comment(val) def handle_multiline_comment(self, val): """ Breaks down a '/* */'-style multi-line comment, and passes the result to handle_comment() @param val: the comment text, as string, including the '/*' and '*/' """ try: # pylint: disable=no-member val = re.match(r"^/\*(.*)\*/$", val, re.DOTALL).group(1) except AttributeError as ex: raise self.parser_error("invalid multi-line comment") from ex # for a comment '/* foo\n * bar\n */', val is now 'foo\n * bar\n ' # however, we'd prefer ' * foo\n * bar' val = ' * ' + val.rstrip() # actually, we'd prefer [' * foo', ' * bar']. lines = val.split('\n') comment_lines = [] for idx, line in enumerate(lines): try: line = re.match(r'^ \*( (.*))?$', line).group(2) or "" except AttributeError as ex: raise self.parser_error("invalid multi-line comment line", idx + self.lineno) from ex # if comment is still empty, don't append anything if comment_lines or line.strip() != "": comment_lines.append(line) self.handle_comment('\n'.join(comment_lines).rstrip()) def handle_comment(self, val): """ Handles any comment, with its format characters removed, extracting the pxd annotation """ annotations = re.findall(r"pxd:\s(.*?)(:pxd|$)", val, re.DOTALL) # pylint: disable=no-member annotations = [annotation[0] for annotation in annotations] if not annotations: raise self.parser_error("comment contains no valid pxd annotation") for annotation in annotations: # remove empty lines at end annotation = annotation.rstrip() annotation_lines = annotation.split('\n') for idx, line in enumerate(annotation_lines): if line.strip() != "": # we've found the first non-empty annotation line self.add_annotation(annotation_lines[idx:]) break else: raise self.parser_error("pxd annotation is empty:\n" + val) def add_annotation(self, annotation_lines): """ Adds a (current namespace, pxd annotation) tuple to self.annotations. """ if "{" in self.stack: raise self.parser_error("PXD annotation is brace-enclosed") if not self.stack: namespace = None else: namespace = "::".join(self.stack) self.annotations.append((namespace, annotation_lines)) def handle_token(self, token, val): """ Handles one token while the parser is in its regular state. Returns the new state integer. """ # accept any token here if token == Token.Keyword and val == 'namespace': # advance to next state on 'namespace' return 1 if (token, val) == (Token.Punctuation, '{'): # we're in a struct/class/... self.stack.append('{') elif (token, val) == (Token.Punctuation, '}'): try: # leave the struct/class/... self.stack.pop() except IndexError as ex: raise self.parser_error("unmatched '}'") from ex elif token == Token.Comment.Single and 'pxd:' in val: self.handle_singleline_comment(val) elif token == Token.Comment.Multiline and 'pxd:' in val: self.handle_multiline_comment(val) else: # we don't care about all those other tokens pass return 0 def parse(self): """ Parses the input file. Internally calls self.tokenize(). Adds all found PXD annotations to self.annotations, together with info about the namespace in which they were encountered. """ def handle_state_0(self, token, val, namespace_parts): del namespace_parts return self.handle_token(token, val) def handle_state_1(self, token, val, namespace_parts): # we're inside a namespace definition; expect Token.Name # TODO: pygments 2.9 correctly reports Token.Name.Namespace # we can require this version eventually and change the condition if token not in Token.Name: raise self.parser_error( "expected identifier after 'namespace'") namespace_parts.append(val) return 2 def handle_state_2(self, token, val, namespace_parts): # either :: or { # the former is for nested namespaces, # the latter ends the namespace if (token, val) == (Token.Operator, ':'): return 3 if (token, val) != (Token.Punctuation, '{'): raise self.parser_error("expected '{' or '::' after " f"'namespace {self.stack[-1]}'") # { found, so fill the stack self.stack.append('::'.join(namespace_parts)) namespace_parts.clear() return 0 def handle_state_3(self, token, val, namespace_parts): del namespace_parts if (token, val) == (Token.Operator, ':'): # now, a name must follow return 1 raise self.parser_error("nested namespaces are separated " "with '::'") transitions = {0: handle_state_0, 1: handle_state_1, 2: handle_state_2, 3: handle_state_3} self.annotations = [] state = 0 # to build up nested namespaces namespace_parts = [] for token, val in self.tokenize(): # ignore whitespaces if token in Token.Text and not val.strip(): continue try: state = transitions[state](self, token, val, namespace_parts) except KeyError as exp: raise ValueError("reached invalid state in pxdgen") from exp if self.stack: raise self.parser_error("expected '}', but found EOF") def get_pxd_lines(self): """ calls self.parse() and processes the pxd annotations to pxd code lines. """ from datetime import datetime year = datetime.now().year yield (f"# Copyright 2013-{year} the openage authors. " "See copying.md for legal info.") yield "" yield ("# Auto-generated from annotations in " + self.filename.name) yield "# " + str(self.filename) self.parse() # namespace of the previous pxd annotation previous_namespace = None for namespace, annotation_lines in self.annotations: yield "" if namespace != previous_namespace: yield "" if namespace: prefix = " " if namespace != previous_namespace: yield ( "cdef extern " "from r\"" + self.filename.as_posix() + "\" " "namespace \"" + namespace + "\" " "nogil" ":" ) else: prefix = "" for annotation in annotation_lines: annotation = self.postprocess_annotation_line(annotation) if annotation: yield prefix + annotation else: # don't emit a line that consists of just the prefix # (avoids trailing whitespace) yield "" previous_namespace = namespace yield "" def postprocess_annotation_line(self, annotation): """ Post-processes each individual annotation line, applying hacks and testing it, etc. See libopenage/pyinterface/hacks.h for documentation on the individual hacks. """ annotation = annotation.rstrip() if annotation.endswith(';'): self.warnings.append( "cython declaration ends in ';', " "what have you done?") if annotation.endswith(')'): self.warnings.append( "mark the function as 'except +' or " "'noexcept':\n" + annotation) elif annotation.endswith('noexcept'): annotation = annotation[:-8].rstrip() if 'cdef ' in annotation: self.warnings.append( "there's no need to use 'cdef' in PXD annotations:\n" + annotation) return annotation def generate(self, pxdfile, ignore_timestamps=False, print_warnings=True): """ reads the input file and writes the output file. the output file is updated only if its content will change. on parsing failure, raises ParserError. """ if not ignore_timestamps and os.path.exists(pxdfile): # skip the file if the timestamp is up to date if os.path.getmtime(self.filename) <= os.path.getmtime(pxdfile): return False result = "\n".join(self.get_pxd_lines()) if os.path.exists(pxdfile): with open(pxdfile, encoding='utf8') as outfile: if outfile.read() == result: # don't write the file if the content is up to date return False if not pxdfile.parent.is_dir(): pxdfile.parent.mkdir() with pxdfile.open('w', encoding='utf8') as outfile: if pxdfile.is_absolute(): printpath = pxdfile else: printpath = os.path.relpath(pxdfile, CWD) print(f"\x1b[36mpxdgen: generate {printpath}\x1b[0m") outfile.write(result) if print_warnings and self.warnings: print(f"\x1b[33;1mWARNING\x1b[m pxdgen[{self.filename}]:") for warning in self.warnings: print(warning) return True def parse_args(): """ pxdgen command-line interface. designed to allow both manual and automatic (via CMake) usage. """ cli = argparse.ArgumentParser() cli.add_argument('files', nargs='*', metavar='HEADERFILE', help="input files (usually cpp .h files).") cli.add_argument('--file-list', help=("a file containing a semicolon-separated " "list of input files.")) cli.add_argument('--ignore-timestamps', action='store_true', help=("force generating even if the output file is already" " up to date")) cli.add_argument('--output-dir', help=("build directory corresponding to the CWD to write" " the generated file(s) in.")) cli.add_argument('-v', '--verbose', action="store_true", help="increase logging verbosity") args = cli.parse_args() if args.file_list: with open(args.file_list, encoding='utf8') as flist: file_list = flist.read().strip().split(';') else: file_list = [] from itertools import chain args.all_files = list(chain(args.files, file_list)) return args def main(): """ CLI entry point """ args = parse_args() cppname = "libopenage" cppdir = Path(cppname).absolute() out_cppdir = Path(args.output_dir) / cppname if args.verbose: hdr_count = len(args.all_files) plural = "s" if hdr_count > 1 else "" print(f"extracting pxd information from {hdr_count} header{plural}...") for filename in args.all_files: filename = Path(filename).resolve() if cppdir not in filename.parents: print(f"pxdgen source file is not in {cppdir!r}: {filename!r}") sys.exit(1) # join out_cppdir with relative path from cppdir pxdfile_relpath = filename.with_suffix('.pxd').relative_to(cppdir) pxdfile = out_cppdir / pxdfile_relpath if args.verbose: print(f"creating '{pxdfile}' for '{filename}':") generator = PXDGenerator(filename) result = generator.generate( pxdfile, ignore_timestamps=args.ignore_timestamps, print_warnings=True ) if args.verbose and not result: print("nothing done.") # create empty __init__.py in all parent directories. # Cython requires this; else it won't find the .pxd files. for dirname in pxdfile_relpath.parents: template = out_cppdir / dirname / "__init__" for extension in ("py", "pxd"): initfile = template.with_suffix("." + extension) if not initfile.exists(): print("\x1b[36mpxdgen: create package index " f"{initfile.relative_to(args.output_dir)}\x1b[0m") initfile.touch() if __name__ == '__main__': main() ================================================ FILE: buildsystem/python.cmake ================================================ # Copyright 2014-2025 the openage authors. See copying.md for legal info. # provides macros for defining python extension modules and pxdgen sources. # and a 'finalize' function that must be called in the end. function(python_init) # filled by pxdgen; written to bin/py/pxdgen_sources for use by pxdgen.py. set_property(GLOBAL PROPERTY SFT_PXDGEN_SOURCES) # filled by add_cython_modules. used by cythonize.py. set_property(GLOBAL PROPERTY SFT_CYTHON_MODULES) set_property(GLOBAL PROPERTY SFT_CYTHON_MODULES_EMBED) # list of pxd files added manually # filled by add_pxds only. for use as depends list for cythonize.py. set_property(GLOBAL PROPERTY SFT_PXD_FILES) # filled by pxdgen. for use as output list for the pxdgen target. set_property(GLOBAL PROPERTY SFT_GENERATED_PXD_FILES) # filled with all .py filenames; used for installing. set_property(GLOBAL PROPERTY SFT_PY_FILES) # filled with the relative source dirs of the above py files. set_property(GLOBAL PROPERTY SFT_PY_FILES_RELSRCDIRS) # filled with all .py filenames that should not be installed. set_property(GLOBAL PROPERTY SFT_PY_FILES_NOINSTALL) # filled with all .py filenames that are installed to the elf binary directory. set_property(GLOBAL PROPERTY SFT_PY_FILES_BININSTALL) # filled with replacement names that are used for installing each entry in SFT_PY_FILES # special value __nope is used if no replacement should be done. # thank you cmake for not providing a dict # where i could just look the original name up to get the replacement name. set_property(GLOBAL PROPERTY SFT_PY_FILES_INSTALLNAMES) # filled with all cython module target names; used for in-place creation. set_property(GLOBAL PROPERTY SFT_CYTHON_MODULE_TARGETS) endfunction() function(cython_get_target_name RESULT_VAR) file(RELATIVE_PATH REL_CURRENT_SOURCE_DIR "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") get_filename_component(OUTPUTNAME "${source}" NAME_WE) set(TARGETNAME "${REL_CURRENT_SOURCE_DIR}/${OUTPUTNAME}") string(REPLACE "/" "_" TARGETNAME "${TARGETNAME}") set("${RESULT_VAR}" "${TARGETNAME}" PARENT_SCOPE) endfunction() function(cython_modname_sanitize RESULT_VAR SOURCE) if(NOT IS_ABSOLUTE "${SOURCE}") set(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}") endif() if(NOT "${SOURCE}" MATCHES ".*\\.pyx?$") message(FATAL_ERROR "non-.py/.pyx file given: ${SOURCE}") endif() set("${RESULT_VAR}" "${SOURCE}" PARENT_SCOPE) endfunction() function(add_cython_modules) # adds a new module for cython compilation, by relative filename. # synoposis: # add_cython_modules( # test.pyx # EMBED __main__.pyx # STANDALONE EMBED foo/bar.pyx # NOINSTALL STANDALONE foo/test.pyx # ) # # test.pyx is compiled to a shared library linked against PYEXT_LINK_LIBRARY # __main__.pyx is compiled to a executable with embedded python interpreter, # linked against libpython and PYEXT_LINK_LIBRARY. # foo/bar.pyx is compiled to a executable with embedded python interpreter, # linked only against libpython. # foo/test.pyx is compiled to a shared library linked against nothing, and will # not be installed. # # PYEXT_LINK_LIBRARY is linked to all non-standalone cython modules, # and is used e.g. to inject compiler options file(RELATIVE_PATH REL_CURRENT_SOURCE_DIR "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") set(EMBED_NEXT FALSE) set(STANDALONE_NEXT FALSE) set(NOINSTALL_NEXT FALSE) foreach(source ${ARGN}) if(source STREQUAL "EMBED") set(EMBED_NEXT TRUE) elseif(source STREQUAL "STANDALONE") set(STANDALONE_NEXT TRUE) elseif(source STREQUAL "NOINSTALL") set(NOINSTALL_NEXT TRUE) else() cython_modname_sanitize(source "${source}") # construct some hopefully unique target name cython_get_target_name(TARGETNAME "${source}") get_filename_component(OUTPUTNAME "${source}" NAME_WE) set(CPPNAME "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUTNAME}.cpp") set_source_files_properties("${CPPNAME}" PROPERTIES GENERATED ON) # generate the pretty module name set(PRETTY_MODULE_NAME "${REL_CURRENT_SOURCE_DIR}/${OUTPUTNAME}") string(REPLACE "/" "." PRETTY_MODULE_NAME "${PRETTY_MODULE_NAME}") string(REGEX REPLACE "^\\." "" PRETTY_MODULE_NAME "${PRETTY_MODULE_NAME}") set(PRETTY_MODULE_PROPERTIES "") if(EMBED_NEXT) set(PRETTY_MODULE_PROPERTIES "${PRETTY_MODULE_PROPERTIES} [embedded interpreter]") set_property(GLOBAL APPEND PROPERTY SFT_CYTHON_MODULES_EMBED "${source}") add_executable("${TARGETNAME}" "${CPPNAME}") if(MINGW) set_target_properties("${TARGETNAME}" PROPERTIES LINK_FLAGS "-municode") endif() target_link_libraries("${TARGETNAME}" PRIVATE ${PYEXT_LIBRARY}) else() set_property(GLOBAL APPEND PROPERTY SFT_CYTHON_MODULES "${source}") add_library("${TARGETNAME}" MODULE "${CPPNAME}") set_target_properties("${TARGETNAME}" PROPERTIES PREFIX "" SUFFIX "${PYEXT_SUFFIX}" ) if(WIN32) target_link_libraries("${TARGETNAME}" PRIVATE ${PYEXT_LIBRARY}) endif() endif() if(NOINSTALL_NEXT) set(PRETTY_MODULE_PROPERTIES "${PRETTY_MODULE_PROPERTIES} [noinstall]") else() install( TARGETS "${TARGETNAME}" DESTINATION "${CMAKE_PY_INSTALL_PREFIX}/${REL_CURRENT_SOURCE_DIR}" ) endif() set_target_properties("${TARGETNAME}" PROPERTIES COMPILE_FLAGS "${PYEXT_CXXFLAGS}" INCLUDE_DIRECTORIES "${PYEXT_INCLUDE_DIRS}" OUTPUT_NAME "${OUTPUTNAME}" ) if (STANDALONE_NEXT) set(PRETTY_MODULE_PROPERTIES "${PRETTY_MODULE_PROPERTIES} [standalone]") else() set_target_properties("${TARGETNAME}" PROPERTIES LINK_DEPENDS_NO_SHARED 1) target_link_libraries("${TARGETNAME}" PRIVATE "${PYEXT_LINK_LIBRARY}") endif() # Since this module is not embedded with python interpreter, # Mac OS X requires a link flag to resolve undefined symbols if(NOT EMBED_NEXT AND APPLE) set_target_properties("${TARGETNAME}" PROPERTIES LINK_FLAGS "-undefined dynamic_lookup" ) endif() add_dependencies("${TARGETNAME}" cythonize) set_property(GLOBAL APPEND PROPERTY SFT_CYTHON_MODULE_TARGETS "${TARGETNAME}") pretty_print_target("cython module" "${PRETTY_MODULE_NAME}" "${PRETTY_MODULE_PROPERTIES}") # Reset the flags before processing the next cython module set(EMBED_NEXT FALSE) set(NOINSTALL_NEXT FALSE) set(STANDALONE_NEXT FALSE) endif() endforeach() endfunction() function(pyext_link_libraries SOURCE) # link a cython module to some libraries # # synoposis: # pyext_link_libraries( # somemodule.pyx # PNG::PNG # ${OPUS_LIBRARIES} # ) cython_modname_sanitize(source "${SOURCE}") cython_get_target_name(TARGETNAME "${SOURCE}") foreach(library ${ARGN}) target_link_libraries("${TARGETNAME}" PRIVATE "${library}") endforeach() endfunction() function(pyext_include_directories SOURCE) # build a cython module with given include dirs # # synoposis: # pyext_include_directories( # somemodule.pyx # ${OPUS_INCLUDE_DIRS} # ) cython_modname_sanitize(source "${SOURCE}") cython_get_target_name(TARGETNAME "${SOURCE}") foreach(directory ${ARGN}) target_include_directories("${TARGETNAME}" PRIVATE "${directory}") endforeach() endfunction() function(pxdgen) # add a C++ header file as pxdgen source file foreach(source ${ARGN}) if(NOT IS_ABSOLUTE "${source}") set(source "${CMAKE_CURRENT_SOURCE_DIR}/${source}") endif() get_filename_component(source_ext "${source}" EXT) if(NOT "${source_ext}" STREQUAL ".h") message(FATAL_ERROR "non-.h file given to pxdgen: ${source}") endif() # TODO: change if multiple files with the same name are supported in a directory get_filename_component(source_name_without_ext "${source}" NAME_WE) set(PXDNAME "${CMAKE_CURRENT_BINARY_DIR}/${source_name_without_ext}.pxd") set_source_files_properties("${PXDNAME}" PROPERTIES GENERATED ON) set_property(GLOBAL APPEND PROPERTY SFT_PXDGEN_SOURCES "${source}") set_property(GLOBAL APPEND PROPERTY SFT_GENERATED_PXD_FILES "${PXDNAME}") endforeach() endfunction() function(add_pxds) # add a .pxd or other additional Cython source foreach(source ${ARGN}) if(NOT IS_ABSOLUTE "${source}") set(source "${CMAKE_CURRENT_SOURCE_DIR}/${source}") endif() if(NOT "${source}" MATCHES ".*\\.px[id]$") message(FATAL_ERROR "non-pxd/pxi file given to add_pxds: ${source}") endif() set_property(GLOBAL APPEND PROPERTY SFT_PXD_FILES "${source}") endforeach() endfunction() function(add_py_modules) # add a .py file for installing # # add_py_module ( # somefile.py # NOINSTALL otherfile.py # do not install this file # BININSTALL wtf.py # install to $PREFIX/bin/wtf.py # BININSTALL lol.py AS rofl # install lol.py to $PREFIX/bin/rofl # ) # # because cmake is such a nice language, # the handling of the above is quite painful. file(RELATIVE_PATH REL_CURRENT_SOURCE_DIR "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") set(NOINSTALL_NEXT FALSE) set(BININSTALL_NEXT FALSE) set(AS_NEXT FALSE) set(IN_BININSTALL FALSE) foreach(source ${ARGN}) # keyword parsing so the next arg knows # what the previous keyword was. if(source STREQUAL "NOINSTALL") set(NOINSTALL_NEXT TRUE) elseif(source STREQUAL "BININSTALL") set(BININSTALL_NEXT TRUE) elseif(source STREQUAL "AS") set(AS_NEXT TRUE) else() if(NOT ${source} MATCHES ".*\\.py$" AND NOT AS_NEXT) message(FATAL_ERROR "non-Python file given to add_py_modules: ${source}") endif() if(IN_BININSTALL AND NOT AS_NEXT) # if after the source file for /bin installation no AS # follows, normal source listings follow. set(IN_BININSTALL FALSE) endif() if(NOT IS_ABSOLUTE "${source}" AND NOT AS_NEXT) # make the source absolute, except when source is the "AS $replacementname" set(source "${CMAKE_CURRENT_SOURCE_DIR}/${source}") endif() if(NOINSTALL_NEXT OR BININSTALL_NEXT) # if source should not be installed or be installed to bin/ # it is excluded from the big python installation list set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES_NOINSTALL "${source}") set(NOINSTALL_NEXT FALSE) endif() if(BININSTALL_NEXT) # if the file is to be installed to bin/ set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES_BININSTALL "${source}") set(BININSTALL_NEXT FALSE) set(IN_BININSTALL TRUE) endif() if(AS_NEXT) # the replacement name must be after the BININSTALL definition if(NOT IN_BININSTALL) message(FATAL_ERROR "you used AS without BININSTALL!") endif() set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES_INSTALLNAMES "${source}") else() # in all cases except the "AS $newname", # add the python file to the to-compile list. if("${REL_CURRENT_SOURCE_DIR}" STREQUAL "") set(REL_CURRENT_SOURCE_DIR "./") endif() set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES "${source}") set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES_RELSRCDIRS "${REL_CURRENT_SOURCE_DIR}") endif() if(NOT IN_BININSTALL) # don't replace the install name if we're in the "AS $name" # or no AS followed the BININSTALL set_property(GLOBAL APPEND PROPERTY SFT_PY_FILES_INSTALLNAMES "__nope") endif() if(AS_NEXT) # reset the alias and bin-install flags # so they count only for one (the current) source file. set(AS_NEXT FALSE) set(IN_BININSTALL FALSE) endif() endif() endforeach() endfunction() function(python_finalize) # pxdgen (.h -> .pxd) get_property(pxdgen_sources GLOBAL PROPERTY SFT_PXDGEN_SOURCES) write_on_change("${CMAKE_BINARY_DIR}/py/pxdgen_sources" "${pxdgen_sources}") set(PXDGEN_TIMEFILE "${CMAKE_BINARY_DIR}/py/pxdgen_timefile") add_custom_command(OUTPUT "${PXDGEN_TIMEFILE}" COMMAND "${PYTHON}" -m buildsystem.pxdgen --file-list "${CMAKE_BINARY_DIR}/py/pxdgen_sources" --output-dir "${CMAKE_BINARY_DIR}" COMMAND "${CMAKE_COMMAND}" -E touch "${PXDGEN_TIMEFILE}" DEPENDS ${pxdgen_sources} "${CMAKE_BINARY_DIR}/py/pxdgen_sources" COMMENT "pxdgen: generating .pxd files from headers" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) add_custom_target(pxdgen ALL DEPENDS "${PXDGEN_TIMEFILE}") # cythonize (.pyx -> .cpp) get_property(cython_modules GLOBAL PROPERTY SFT_CYTHON_MODULES) write_on_change("${CMAKE_BINARY_DIR}/py/cython_modules" "${cython_modules}") get_property(cython_modules_embed GLOBAL PROPERTY SFT_CYTHON_MODULES_EMBED) write_on_change("${CMAKE_BINARY_DIR}/py/cython_modules_embed" "${cython_modules_embed}") get_property(pxd_list GLOBAL PROPERTY SFT_PXD_FILES) get_property(generated_pxd_list GLOBAL PROPERTY SFT_GENERATED_PXD_FILES) write_on_change("${CMAKE_BINARY_DIR}/py/pxd_list" "${pxd_list};${generated_pxd_list}") set(CYTHONIZE_TIMEFILE "${CMAKE_BINARY_DIR}/py/cythonize_timefile") add_custom_command(OUTPUT "${CYTHONIZE_TIMEFILE}" # remove unneeded files from the previous builds (if any) COMMAND "${CMAKE_COMMAND}" -E remove -f "${CMAKE_BINARY_DIR}/__init__.py" "${CMAKE_BINARY_DIR}/__init__.pxd" COMMAND "${PYTHON}" -m buildsystem.cythonize "${CMAKE_BINARY_DIR}/py/cython_modules" "${CMAKE_BINARY_DIR}/py/cython_modules_embed" "${CMAKE_BINARY_DIR}/py/pxd_list" ${force_optimized_lib_flag} "--build-dir" "${CMAKE_BINARY_DIR}" COMMAND "${CMAKE_COMMAND}" -E touch "${CYTHONIZE_TIMEFILE}" DEPENDS "${PXDGEN_TIMEFILE}" ${cython_modules} ${cython_modules_embed} ${pxd_list} "${CMAKE_BINARY_DIR}/py/cython_modules" "${CMAKE_BINARY_DIR}/py/cython_modules_embed" "${CMAKE_BINARY_DIR}/py/pxd_list" COMMENT "cythonize.py: compiling .pyx files to .cpp" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) add_custom_target(cythonize ALL DEPENDS "${CYTHONIZE_TIMEFILE}") # py compile (.py -> .pyc) get_property(py_files GLOBAL PROPERTY SFT_PY_FILES) get_property(py_files_srcdirs GLOBAL PROPERTY SFT_PY_FILES_RELSRCDIRS) get_property(py_files_noinstall GLOBAL PROPERTY SFT_PY_FILES_NOINSTALL) get_property(py_files_bininstall GLOBAL PROPERTY SFT_PY_FILES_BININSTALL) get_property(py_install_names GLOBAL PROPERTY SFT_PY_FILES_INSTALLNAMES) write_on_change("${CMAKE_BINARY_DIR}/py/py_files" "${py_files}") set(COMPILEPY_TIMEFILE "${CMAKE_BINARY_DIR}/py/compilepy_timefile") set(COMPILEPY_INVOCATION "${PYTHON}" -m buildsystem.compilepy "${CMAKE_BINARY_DIR}/py/py_files" "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" ) # determine the compiled file name for all source files execute_process(COMMAND ${COMPILEPY_INVOCATION} "--print-output-paths-only" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE py_compiled_files RESULT_VARIABLE COMMAND_RESULT ) if(NOT ${COMMAND_RESULT} EQUAL 0) message(FATAL_ERROR "failed to get output list from compilepy invocation") endif() string(STRIP "${py_compiled_files}" py_compiled_files) add_custom_command(OUTPUT "${COMPILEPY_TIMEFILE}" COMMAND ${COMPILEPY_INVOCATION} COMMAND "${CMAKE_COMMAND}" -E touch "${COMPILEPY_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" DEPENDS "${CMAKE_BINARY_DIR}/py/py_files" ${py_files} COMMENT "compiling .py files to .pyc files" ) add_custom_target(compilepy ALL DEPENDS "${COMPILEPY_TIMEFILE}") list(LENGTH py_files py_files_count) math(EXPR py_files_count_range "${py_files_count} - 1") foreach(idx RANGE ${py_files_count_range}) list(GET py_files ${idx} pyfile) list(GET py_compiled_files ${idx} pycfile) list(GET py_files_srcdirs ${idx} pyrelsrcdir) list(GET py_install_names ${idx} pyinstallname) file(TO_CMAKE_PATH ${pycfile} pycfile) list(FIND py_files_noinstall "${pyfile}" do_install) list(FIND py_files_bininstall "${pyfile}" do_bininstall) if(do_install LESS 0) install( FILES "${pyfile}" DESTINATION "${CMAKE_PY_INSTALL_PREFIX}/${pyrelsrcdir}" ) install( FILES "${pycfile}" DESTINATION "${CMAKE_PY_INSTALL_PREFIX}/${pyrelsrcdir}/__pycache__" ) elseif(do_bininstall GREATER -1) # install the python file to the elf binary dir if("${pyinstallname}" STREQUAL "__nope") install( PROGRAMS "${pyfile}" DESTINATION "${CMAKE_INSTALL_BINDIR}" ) else() # optionally, rename the file: install( PROGRAMS "${pyfile}" RENAME "${pyinstallname}" DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() endif() endforeach() ################################################## # code generation codegen_run() # inplace module install (bin/module.so -> module.so) # MSVC, Xcode (multi-config generators) produce outputs # in a directory different from `CMAKE_CURRENT_BINARY_DIR`. # link/copy the output files as required for the cython modules. get_property(cython_module_targets GLOBAL PROPERTY SFT_CYTHON_MODULE_TARGETS) set(cython_module_files_expr) foreach(cython_module_target ${cython_module_targets}) list(APPEND cython_module_files_expr "$") endforeach() set(INPLACEMODULES_LISTFILE "${CMAKE_BINARY_DIR}/py/inplace_module_list_$") file(GENERATE OUTPUT ${INPLACEMODULES_LISTFILE} CONTENT "${cython_module_files_expr}" ) set(INPLACEMODULES_INVOCATION "${PYTHON}" -m buildsystem.inplacemodules ${INPLACEMODULES_LISTFILE} "$" ) set(INPLACEMODULES_TIMEFILE "${CMAKE_BINARY_DIR}/py/inplacemodules_timefile") add_custom_command(OUTPUT "${INPLACEMODULES_TIMEFILE}" COMMAND ${INPLACEMODULES_INVOCATION} DEPENDS ${INPLACEMODULES_LISTFILE} ${cython_module_targets} COMMAND "${CMAKE_COMMAND}" -E touch "${INPLACEMODULES_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" COMMENT "creating in-place modules" ) add_custom_target(inplacemodules ALL DEPENDS "${INPLACEMODULES_TIMEFILE}") # cleaning of all in-sourcedir stuff add_custom_target(cleancython COMMAND ${INPLACEMODULES_INVOCATION} --clean COMMAND "${PYTHON}" -m buildsystem.cythonize --clean "${CMAKE_BINARY_DIR}/py/cython_modules" "${CMAKE_BINARY_DIR}/py/cython_modules_embed" "${CMAKE_BINARY_DIR}/py/pxd_list" "--build-dir" "${CMAKE_BINARY_DIR}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) add_custom_command(TARGET cleancython POST_BUILD # general deleters to catch files that have already been un-listed. COMMAND find openage -name "'*.cpp'" -type f -print -delete COMMAND find openage -name "'*.html'" -type f -print -delete COMMAND find openage -name "'*.so'" -type f -print -delete COMMAND "${CMAKE_COMMAND}" -E remove "${CYTHONIZE_TIMEFILE}" COMMAND "${CMAKE_COMMAND}" -E remove "${INPLACEMODULES_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" ) add_custom_target(cleanpxdgen COMMAND find libopenage -name "'*.pxd'" -type f -print -delete COMMAND find libopenage -name "__init__.py" -type f -print -delete COMMAND "${CMAKE_COMMAND}" -E remove "${PXDGEN_TIMEFILE}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" ) # check for any unlisted .py files, and error. execute_process( COMMAND "${PYTHON}" -m buildsystem.check_py_file_list "${CMAKE_BINARY_DIR}/py/py_files" "${CMAKE_SOURCE_DIR}/openage" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res ) if(NOT res EQUAL 0) message(FATAL_ERROR ".py file listing inconsistent") endif() endfunction() python_init() ================================================ FILE: buildsystem/scripts/EmbedFont.cmake ================================================ # Copyright 2017-2017 the openage authors. See copying.md for legal info. if(NOT forward_variables) message(FATAL_ERROR "CMake configuration variables not available. Please include(ForwardVariables.cmake)") endif() include("${buildsystem_dir}/modules/DownloadCache.cmake") set(dejavu_dir "${downloads_dir}/DejaVu") set(dejavu_zip "dejavu-fonts-ttf-2.37.tar.bz2") set(dejavu_zip_download_path "${dejavu_dir}/${dejavu_zip}") # TODO: disable downloading for CI builds. download_cache( "https://github.com/dejavu-fonts/dejavu-fonts/releases/download/version_2_37/${dejavu_zip}" "${dejavu_zip_download_path}" ) execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xf "${dejavu_zip_download_path}" WORKING_DIRECTORY "${dejavu_dir}" ) file(GLOB_RECURSE dejavu_fonts "${dejavu_dir}/DejaVuSerif*.ttf") file(COPY ${dejavu_fonts} DESTINATION "${CMAKE_INSTALL_PREFIX}/share/fonts") file(GLOB_RECURSE dejavu_conf "${dejavu_dir}/*dejavu-serif.conf") file(COPY ${dejavu_conf} DESTINATION "${CMAKE_INSTALL_PREFIX}/${py_install_prefix}/fonts/conf.d") ================================================ FILE: buildsystem/scripts/EmbedPython.cmake ================================================ # Copyright 2017-2022 the openage authors. See copying.md for legal info. if(NOT forward_variables) message(FATAL_ERROR "CMake configuration variables not available. Please include(ForwardVariables.cmake)") endif() include("${buildsystem_dir}/modules/DownloadCache.cmake") if(sizeof_void_p EQUAL 8) set(py_arch "amd64") else() set(py_arch "win32") endif() set(python_embed_zip "python-${py_version}-embed-${py_arch}.zip") set(python_zip_download_path "${downloads_dir}/${python_embed_zip}") # TODO: disable downloading for CI builds. download_cache( "https://www.python.org/ftp/python/${py_version}/${python_embed_zip}" "${python_zip_download_path}" ) execute_process(COMMAND "${CMAKE_COMMAND}" -E tar xf "${python_zip_download_path}" WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/${py_install_prefix}" ) execute_process(COMMAND "${PYTHON}" "${buildsystem_dir}/scripts/copy_modules.py" numpy PIL pyreadline3 readline "${CMAKE_INSTALL_PREFIX}/${py_install_prefix}" ) ================================================ FILE: buildsystem/scripts/EmbedWinDependencies.cmake ================================================ # Copyright 2017-2020 the openage authors. See copying.md for legal info. if(NOT forward_variables) message(FATAL_ERROR "CMake configuration variables not available. Please include(ForwardVariables.cmake)") endif() get_filename_component(cl_directory "${cmake_linker}" DIRECTORY) find_program(dumpbin dumpbin.exe PATHS "${cl_directory}") if(NOT dumpbin) message(SEND_ERROR "Cannot locate dumpbin.") return() endif() # Paths for applocal and powershell set(APPLOCAL_SCRIPT "${vcpkg_dir}/../../scripts/buildsystems/msbuild/applocal.ps1") set(POWERSHELL_COMMAND "$ENV{SYSTEMROOT}/System32/WindowsPowerShell/v1.0/powershell.exe") # function to wrap the dependency resolving and copying logic on Windows. # Inspired by `applocal.ps1` from vcpkg which recursively descends into the # dependency tree of a binary and copies each to the target directory. function(resolve binary) get_filename_component(bin_dir "${binary}" DIRECTORY) execute_process(COMMAND "${dumpbin}" /DEPENDENTS "${binary}" RESULT_VARIABLE dump_result OUTPUT_VARIABLE dump_output ) if(NOT dump_result EQUAL 0) message(SEND_ERROR "dumpbin returned ${dump_result}.") return() endif() string(REGEX MATCHALL " [^\\\n]+\\.dll" dll_list ${dump_output}) foreach(item ${dll_list}) string(STRIP "${item}" dll_name) set(dll_src_path "${vcpkg_dir}/bin/${dll_name}") set(dll_dest_path "${bin_dir}/${dll_name}") if(NOT EXISTS "${dll_dest_path}" AND EXISTS "${dll_src_path}") message(STATUS "Copying ${dll_name}") file(COPY "${dll_src_path}" DESTINATION "${bin_dir}") resolve("${dll_dest_path}") endif() endforeach() endfunction() # windeployqt if(use_windeployqt AND windeployqt) foreach(file ${CMAKE_INSTALL_MANIFEST_FILES}) if((file MATCHES "\\.dll$") AND NOT ("${file}" STREQUAL "nyan.dll")) resolve("${file}") if(windeployqt) message(STATUS "Calling windeployqt with ${file}.") execute_process(COMMAND "${CMAKE_COMMAND}" -E env "PATH=${vcpkg_dir}/bin;$ENV{PATH}" "${windeployqt}" --qmldir "${CMAKE_INSTALL_PREFIX}/${asset_dir}/qml" --dir "${CMAKE_INSTALL_PREFIX}/bin/qml" --plugindir "${CMAKE_INSTALL_PREFIX}/bin/plugins" --libdir "${CMAKE_INSTALL_PREFIX}/bin" --list "target" --verbose 2 "${file}" ) endif() endif() endforeach() endif() # applocal if((EXISTS ${POWERSHELL_COMMAND}) AND (EXISTS ${APPLOCAL_SCRIPT})) foreach(file ${CMAKE_INSTALL_MANIFEST_FILES}) if(file MATCHES "\\.dll$") message(STATUS "Calling applocal.ps1 with ${file}.") execute_process(COMMAND "${POWERSHELL_COMMAND}" "${APPLOCAL_SCRIPT}" -targetBinary "${file}" -installedDir "${vcpkg_dir}/bin" -tlogFile "${LOGFILES_DIR}/applocal.log" -copiedFilesLog "${LOGFILES_DIR}/applocal_copied_files.log" ERROR_FILE "${LOGFILES_DIR}/applocal_error.log" OUTPUT_FILE "${LOGFILES_DIR}/applocal_output.log" ) endif() endforeach() endif() # Copy qt.conf to python-directory file(COPY "${buildsystem_dir}/templates/qt.conf" DESTINATION "${CMAKE_INSTALL_PREFIX}/${py_install_prefix}" ) # The fonts directory defaults to the same directory as the executable. # In this case, it is python.exe at `py_install_prefix`. file(COPY "${vcpkg_dir}/tools/fontconfig/fonts" DESTINATION "${CMAKE_INSTALL_PREFIX}/${py_install_prefix}" ) include("${CMAKE_CURRENT_LIST_DIR}/EmbedFont.cmake") include("${CMAKE_CURRENT_LIST_DIR}/EmbedPython.cmake") ================================================ FILE: buildsystem/scripts/copy_modules.py ================================================ # Copyright 2017-2020 the openage authors. See copying.md for legal info. """ Copies the specified modules to the required directory. Used for packaging the python dependencies. """ import argparse import importlib import importlib.abc import importlib.util import os import shutil import sys def copy_module(name, destination): """Copy the importable module 'name' to the 'destination' directory""" loader = importlib.util.find_spec(name).loader if not isinstance(loader, importlib.abc.FileLoader): sys.exit(f"Loader for module {name} is not handled") print(f'Copying "{name}" to "{destination}"') filename = loader.get_filename(name) if loader.is_package(name): pkgdir, _ = os.path.split(filename) shutil.copytree(pkgdir, os.path.join(destination, name)) else: shutil.copy2(filename, destination) def main(): """ CLI entry point """ cli = argparse.ArgumentParser() cli.add_argument("pymodule_name", nargs='+', help=( "list of all modules that shall be copied" )) cli.add_argument("dest_dir", help=( "destination directory where modules will be copied" )) args = cli.parse_args() for module in args.pymodule_name: copy_module(module, args.dest_dir) if __name__ == '__main__': main() ================================================ FILE: buildsystem/templates/Doxyfile.in ================================================ # Doxyfile 1.8.12 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = @PROJECT_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @PROJECT_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = @CMAKE_SOURCE_DIR@/assets/logo/banner.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = YES # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = YES # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 0. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @DOXYGEN_SCAN_FOLDERS@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. FILE_PATTERNS = *.cpp \ *.h \ *.py \ *.pyx \ *.pxd \ *.in \ *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = "@CMAKE_SOURCE_DIR@/buildsystem/doxygen_custom.css" # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /