diff --git a/src/rutile/core/ui/backend/rmlui_input_handler.cppm b/src/rutile/core/ui/backend/rmlui_input_handler.cppm index 41622ff..2dedff2 100644 --- a/src/rutile/core/ui/backend/rmlui_input_handler.cppm +++ b/src/rutile/core/ui/backend/rmlui_input_handler.cppm @@ -1,11 +1,8 @@ module; -#include -#include -#include - export module rutile.core.ui.backend.rmlui_input_handler; +import wrappers.rmlui.core; import wrappers.sdl; export namespace rutile diff --git a/src/rutile/core/ui/backend/rmlui_render_interface.cppm b/src/rutile/core/ui/backend/rmlui_render_interface.cppm index f64528f..28724c9 100644 --- a/src/rutile/core/ui/backend/rmlui_render_interface.cppm +++ b/src/rutile/core/ui/backend/rmlui_render_interface.cppm @@ -3,15 +3,13 @@ module; -#include -#include -#include -#include +#include #include export module rutile.core.ui.backend.rmlui_render_interface; import std; +import wrappers.rmlui.core; import wrappers.sdl; import wrappers.sdl_image; @@ -152,7 +150,7 @@ export namespace rutile const Rml::Vector2i source_dimensions ) override { - RMLUI_ASSERT( + assert( source.data() && source.size() == static_cast(source_dimensions.x * source_dimensions.y * 4) ); diff --git a/src/rutile/core/ui/backend/rmlui_system_interface.cppm b/src/rutile/core/ui/backend/rmlui_system_interface.cppm index 6c74193..3f2ac72 100644 --- a/src/rutile/core/ui/backend/rmlui_system_interface.cppm +++ b/src/rutile/core/ui/backend/rmlui_system_interface.cppm @@ -3,14 +3,13 @@ module; -#include -#include -#include #include export module rutile.core.ui.backend.rmlui_system_interface; +import std; import utils.memory; +import wrappers.rmlui.core; import wrappers.sdl; // Shorthand for a unique pointer to an SDL_Cursor diff --git a/src/rutile/core/ui/ui_server.cppm b/src/rutile/core/ui/ui_server.cppm index 66eb4c9..3e5d3ab 100644 --- a/src/rutile/core/ui/ui_server.cppm +++ b/src/rutile/core/ui/ui_server.cppm @@ -1,8 +1,6 @@ module; #include -#include -#include export module rutile.core.ui.ui_server; @@ -10,6 +8,8 @@ import std; import rutile.core.ui.backend.rmlui_render_interface; import rutile.core.ui.backend.rmlui_system_interface; import rutile.core.ui.backend.rmlui_input_handler; +import wrappers.rmlui.core; +import wrappers.rmlui.debugger; import wrappers.sdl; export namespace rutile diff --git a/src/rutile/game/game.cppm b/src/rutile/game/game.cppm index 8b7a1ff..9f8ca08 100644 --- a/src/rutile/game/game.cppm +++ b/src/rutile/game/game.cppm @@ -1,7 +1,6 @@ module; #include -#include export module rutile.game.game; @@ -11,6 +10,7 @@ import rutile.core.drawing.tile_map; import rutile.core.drawing.tile_set; import rutile.core.engine; import rutile.core.render_server; +import wrappers.rmlui.core; import wrappers.sdl; import wrappers.sdl_image; diff --git a/src/wrappers/rmlui/core.cppm b/src/wrappers/rmlui/core.cppm new file mode 100644 index 0000000..fa209d4 --- /dev/null +++ b/src/wrappers/rmlui/core.cppm @@ -0,0 +1,530 @@ +module; + +#include + +export module wrappers.rmlui.core; + +export +{ + namespace Rml + { + using Rml::Animation; + using Rml::AnimationList; + using Rml::Any; + using Rml::Array; + using Rml::ArrayDefinition; + using Rml::Assert; + using Rml::AttributeNameList; + using Rml::BasePointerDefinition; + using Rml::BaseXMLParser; + using Rml::BlendMode; + using Rml::Box; + using Rml::BoxArea; + using Rml::BoxDirection; + using Rml::BoxEdge; + using Rml::BoxShadow; + using Rml::BoxShadowList; + using Rml::CallbackTexture; + using Rml::CallbackTextureFunction; + using Rml::CallbackTextureInterface; + using Rml::CallbackTextureSource; + using Rml::Character; + using Rml::ClipMaskGeometry; + using Rml::ClipMaskGeometryList; + using Rml::ClipMaskOperation; + using Rml::ColorFormat; + using Rml::ColorStop; + using Rml::ColorStopList; + using Rml::Colour; + using Rml::Colourb; + using Rml::ColourbPremultiplied; + using Rml::Colourf; + using Rml::ColumnMajorMatrix4f; + using Rml::ColumnMajorStorage; + using Rml::CompiledFilter; + using Rml::CompiledFilterHandle; + using Rml::CompiledGeometryHandle; + using Rml::CompiledShader; + using Rml::CompiledShaderHandle; + using Rml::ComputedValues; + using Rml::ContainerBox; + using Rml::Context; + using Rml::ContextInstancer; + using Rml::ContextPtr; + using Rml::ConvolutionFilter; + using Rml::CornerSizes; + using Rml::CreateContext; + using Rml::CreateString; + using Rml::DataAddress; + using Rml::DataAddressEntry; + using Rml::DataController; + using Rml::DataControllerInstancer; + using Rml::DataControllerPtr; + using Rml::DataEventFunc; + using Rml::DataGetFunc; + using Rml::DataModel; + using Rml::DataModelConstructor; + using Rml::DataModelHandle; + using Rml::DataSetFunc; + using Rml::DataTransformFunc; + using Rml::DataTypeGetFunc; + using Rml::DataTypeRegister; + using Rml::DataTypeSetFunc; + using Rml::DataVariable; + using Rml::DataVariableType; + using Rml::DataView; + using Rml::DataViewInstancer; + using Rml::DataViewPtr; + using Rml::Decorator; + using Rml::DecoratorDataHandle; + using Rml::DecoratorDeclaration; + using Rml::DecoratorDeclarationList; + using Rml::DecoratorInstancer; + using Rml::DecoratorInstancerInterface; + using Rml::DecoratorPtrList; + using Rml::DecoratorsPtr; + using Rml::DefaultActionPhase; + using Rml::DefaultStyleSheetParsers; + using Rml::Dictionary; + using Rml::DirtyVariables; + using Rml::DocumentHeader; + using Rml::EdgeSizes; + using Rml::EffectSpecification; + using Rml::Element; + using Rml::ElementAnimation; + using Rml::ElementAnimationList; + using Rml::ElementAttributes; + using Rml::ElementBackgroundBorder; + using Rml::ElementDefinition; + using Rml::ElementDocument; + using Rml::ElementForm; + using Rml::ElementFormControl; + using Rml::ElementFormControlInput; + using Rml::ElementFormControlSelect; + using Rml::ElementFormControlTextArea; + using Rml::ElementInstancer; + using Rml::ElementInstancerElement; + using Rml::ElementInstancerGeneric; + using Rml::ElementInstancerText; + using Rml::ElementList; + using Rml::ElementMeta; + using Rml::ElementProgress; + using Rml::ElementPtr; + using Rml::ElementScroll; + using Rml::ElementStyle; + using Rml::ElementTabSet; + using Rml::ElementText; + using Rml::ElementUtilities; + using Rml::EnableObserverPtr; + using Rml::Event; + using Rml::EventDispatcher; + using Rml::EventId; + using Rml::EventInstancer; + using Rml::EventListener; + using Rml::EventListenerInstancer; + using Rml::EventPhase; + using Rml::EventPtr; + using Rml::EventSpecification; + using Rml::Factory; + using Rml::Family; + using Rml::FamilyBase; + using Rml::FamilyId; + using Rml::FileHandle; + using Rml::FileInterface; + using Rml::Filter; + using Rml::FilterDeclaration; + using Rml::FilterDeclarationList; + using Rml::FilterHandleList; + using Rml::FilterInstancer; + using Rml::FilterOperation; + using Rml::FiltersPtr; + using Rml::FocusFlag; + using Rml::FontEffect; + using Rml::FontEffectInstancer; + using Rml::FontEffectList; + using Rml::FontEffects; + using Rml::FontEffectsHandle; + using Rml::FontEffectsPtr; + using Rml::FontEngineInterface; + using Rml::FontFaceHandle; + using Rml::FontGlyph; + using Rml::FontGlyphMap; + using Rml::FontMetrics; + using Rml::FormatString; + using Rml::FromString; + using Rml::FuncDefinition; + using Rml::Function; + using Rml::Geometry; + using Rml::Get; + using Rml::GetContext; + using Rml::GetFileInterface; + using Rml::GetFontEngineInterface; + using Rml::GetIf; + using Rml::GetNumContexts; + using Rml::GetRenderInterface; + using Rml::GetSystemInterface; + using Rml::GetTextInputHandler; + using Rml::GetTextureSourceList; + using Rml::GetVersion; + using Rml::Hash; + using Rml::Initialise; + using Rml::InlineLevelBox; + using Rml::InputType; + using Rml::IsVoidMemberFunc; + using Rml::KeyframeBlock; + using Rml::Keyframes; + using Rml::KeyframesMap; + using Rml::LayerHandle; + using Rml::LayoutEngine; + using Rml::List; + using Rml::LoadFontFace; + using Rml::Log; + using Rml::MakeLiteralIntVariable; + using Rml::MakeMoveIterator; + using Rml::MakeShared; + using Rml::MakeUnique; + using Rml::Matrix4; + using Rml::Matrix4f; + using Rml::MatrixStorageBase; + using Rml::MediaBlock; + using Rml::MediaBlockList; + using Rml::MediaQueryId; + using Rml::MediaQueryModifier; + using Rml::MemberGetFunc; + using Rml::MemberGetFuncDefinition; + using Rml::MemberGetterFunc; + using Rml::MemberObjectDefinition; + using Rml::MemberScalarGetSetFuncDefinition; + using Rml::MemberSetFunc; + using Rml::MemberSetterFunc; + using Rml::Mesh; + using Rml::MeshUtilities; + using Rml::ModalFlag; + using Rml::NamedDecorator; + using Rml::NamedDecoratorMap; + using Rml::NavigationSearchDirection; + using Rml::NonCopyMoveable; + using Rml::NumericValue; + using Rml::ObserverPtr; + using Rml::OwnedElementList; + using Rml::Pair; + using Rml::ParameterMap; + using Rml::Plugin; + using Rml::PointerDefinition; + using Rml::PointerTraits; + using Rml::PropertiesIterator; + using Rml::PropertiesIteratorView; + using Rml::Property; + using Rml::PropertyDefinition; + using Rml::PropertyDictionary; + using Rml::PropertyId; + using Rml::PropertyIdNameMap; + using Rml::PropertyIdSet; + using Rml::PropertyIdSetIterator; + using Rml::PropertyMap; + using Rml::PropertyParser; + using Rml::PropertySource; + using Rml::PropertySpecification; + using Rml::Queue; + using Rml::Rectangle; + using Rml::Rectanglef; + using Rml::Rectanglei; + using Rml::RegisterEventType; + using Rml::RegisterPlugin; + using Rml::RelativeTarget; + using Rml::Releasable; + using Rml::ReleaseCompiledGeometry; + using Rml::ReleaseFontResources; + using Rml::ReleaseRenderManagers; + using Rml::ReleaseTexture; + using Rml::ReleaseTextures; + using Rml::Releaser; + using Rml::ReleaserBase; + using Rml::RemoveContext; + using Rml::RenderBox; + using Rml::RenderInterface; + using Rml::RenderManager; + using Rml::RenderManagerAccess; + using Rml::RenderState; + using Rml::ReplacedBox; + using Rml::ResolveValue; + using Rml::ResolveValueOr; + using Rml::RowMajorMatrix4f; + using Rml::RowMajorStorage; + using Rml::ScalarDefinition; + using Rml::ScalarFuncDefinition; + using Rml::ScriptInterface; + using Rml::ScriptInterfacePtr; + using Rml::ScriptObject; + using Rml::ScrollAlignment; + using Rml::ScrollBehavior; + using Rml::ScrollController; + using Rml::ScrollIntoViewOptions; + using Rml::ScrollParentage; + using Rml::SetFileInterface; + using Rml::SetFontEngineInterface; + using Rml::SetRenderInterface; + using Rml::SetSystemInterface; + using Rml::SetTextInputHandler; + using Rml::SharedPtr; + using Rml::ShorthandDefinition; + using Rml::ShorthandId; + using Rml::ShorthandIdNameMap; + using Rml::ShorthandType; + using Rml::Shutdown; + using Rml::SmallOrderedMap; + using Rml::SmallOrderedSet; + using Rml::SmallUnorderedMap; + using Rml::SmallUnorderedSet; + using Rml::Span; + using Rml::Sprite; + using Rml::SpriteDefinitionList; + using Rml::SpriteMap; + using Rml::Spritesheet; + using Rml::SpritesheetList; + using Rml::StableMap; + using Rml::StableUnorderedMap; + using Rml::StableVector; + using Rml::StableVectorIndex; + using Rml::Stack; + using Rml::StackingContextChild; + using Rml::Stream; + using Rml::String; + using Rml::StringIteratorU8; + using Rml::StringList; + using Rml::StringView; + using Rml::StructDefinition; + using Rml::StructHandle; + using Rml::StyleSheet; + using Rml::StyleSheetContainer; + using Rml::StyleSheetIndex; + using Rml::StyleSheetNode; + using Rml::StyleSheetParser; + using Rml::StyleSheetSpecification; + using Rml::SystemInterface; + using Rml::TextInputHandler; + using Rml::TextShapingContext; + using Rml::Texture; + using Rml::TextureDatabase; + using Rml::TextureFileIndex; + using Rml::TextureHandle; + using Rml::TextureSource; + using Rml::TexturedMesh; + using Rml::TexturedMeshList; + using Rml::ToString; + using Rml::Touch; + using Rml::TouchId; + using Rml::TouchList; + using Rml::Transform; + using Rml::TransformFuncRegister; + using Rml::TransformPrimitive; + using Rml::TransformPtr; + using Rml::TransformState; + using Rml::Transition; + using Rml::TransitionList; + using Rml::Tween; + using Rml::TypeConverter; + using Rml::URL; + using Rml::UniquePtr; + using Rml::UniqueReleaserPtr; + using Rml::UniqueRenderResource; + using Rml::Unit; + using Rml::Units; + using Rml::UnorderedMap; + using Rml::UnorderedMultimap; + using Rml::UnorderedSet; + using Rml::UnregisterPlugin; + using Rml::VariableDefinition; + using Rml::Variant; + using Rml::VariantList; + using Rml::Vector2; + using Rml::Vector2f; + using Rml::Vector2i; + using Rml::Vector3; + using Rml::Vector3f; + using Rml::Vector3i; + using Rml::Vector4; + using Rml::Vector4f; + using Rml::Vector4i; + using Rml::Vector; + using Rml::Vertex; + using Rml::VoidMemberFunc; + using Rml::WeakPtr; + using Rml::WidgetDropDown; + using Rml::WidgetScroll; + using Rml::WidgetTextInputMultiLine; + using Rml::XMLAttributes; + using Rml::XMLDataType; + using Rml::XMLNodeHandler; + using Rml::XMLParser; + using Rml::byte; + using Rml::is_builtin_data_scalar; + using Rml::voidPtr; + + namespace Input + { + using Rml::Input::KeyIdentifier; + using Rml::Input::KeyModifier; + } + + namespace Math + { + using Rml::Math::ACos; + using Rml::Math::ASin; + using Rml::Math::ATan2; + using Rml::Math::Absolute; + using Rml::Math::Clamp; + using Rml::Math::Cos; + using Rml::Math::DecomposeFractionalIntegral; + using Rml::Math::DegreesToRadians; + using Rml::Math::Exp; + using Rml::Math::ExpandToPixelGrid; + using Rml::Math::HexToDecimal; + using Rml::Math::IsCloseToZero; + using Rml::Math::Lerp; + using Rml::Math::Log2; + using Rml::Math::Max; + using Rml::Math::Min; + using Rml::Math::NormaliseAngle; + using Rml::Math::RadiansToDegrees; + using Rml::Math::RandomBool; + using Rml::Math::RandomInteger; + using Rml::Math::RandomReal; + using Rml::Math::Round; + using Rml::Math::RoundDown; + using Rml::Math::RoundDownToInteger; + using Rml::Math::RoundToInteger; + using Rml::Math::RoundUp; + using Rml::Math::RoundUpToInteger; + using Rml::Math::RoundedLerp; + using Rml::Math::Sin; + using Rml::Math::SnapToPixelGrid; + using Rml::Math::SquareRoot; + using Rml::Math::Tan; + using Rml::Math::ToPowerOfTwo; + } + + namespace StringUtilities + { + using Rml::StringUtilities::BytesUTF8; + using Rml::StringUtilities::ConvertByteOffsetToCharacterOffset; + using Rml::StringUtilities::ConvertCharacterOffsetToByteOffset; + using Rml::StringUtilities::DecodeRml; + using Rml::StringUtilities::EncodeRml; + using Rml::StringUtilities::EndsWith; + using Rml::StringUtilities::ExpandString; + using Rml::StringUtilities::IsWhitespace; + using Rml::StringUtilities::JoinString; + using Rml::StringUtilities::LengthUTF8; + using Rml::StringUtilities::Replace; + using Rml::StringUtilities::SeekBackwardUTF8; + using Rml::StringUtilities::SeekForwardUTF8; + using Rml::StringUtilities::StartsWith; + using Rml::StringUtilities::StringCompareCaseInsensitive; + using Rml::StringUtilities::StripWhitespace; + using Rml::StringUtilities::ToCharacter; + using Rml::StringUtilities::ToLower; + using Rml::StringUtilities::ToUTF8; + using Rml::StringUtilities::ToUpper; + using Rml::StringUtilities::TrimTrailingDotZeros; + } + + namespace Style + { + using Rml::Style::AlignContent; + using Rml::Style::AlignItems; + using Rml::Style::AlignSelf; + using Rml::Style::Bottom; + using Rml::Style::BoxSizing; + using Rml::Style::Clear; + using Rml::Style::Clip; + using Rml::Style::CommonValues; + using Rml::Style::ComputedValues; + using Rml::Style::Direction; + using Rml::Style::Display; + using Rml::Style::Drag; + using Rml::Style::FlexBasis; + using Rml::Style::FlexDirection; + using Rml::Style::FlexWrap; + using Rml::Style::Float; + using Rml::Style::Focus; + using Rml::Style::FontKerning; + using Rml::Style::FontStyle; + using Rml::Style::FontWeight; + using Rml::Style::Height; + using Rml::Style::InheritedValues; + using Rml::Style::JustifyContent; + using Rml::Style::Left; + using Rml::Style::LengthPercentage; + using Rml::Style::LengthPercentageAuto; + using Rml::Style::LineHeight; + using Rml::Style::Margin; + using Rml::Style::MaxHeight; + using Rml::Style::MaxWidth; + using Rml::Style::MinHeight; + using Rml::Style::MinWidth; + using Rml::Style::Nav; + using Rml::Style::NumberAuto; + using Rml::Style::OriginX; + using Rml::Style::OriginY; + using Rml::Style::Overflow; + using Rml::Style::OverscrollBehavior; + using Rml::Style::Padding; + using Rml::Style::PerspectiveOrigin; + using Rml::Style::PointerEvents; + using Rml::Style::Position; + using Rml::Style::RareValues; + using Rml::Style::Right; + using Rml::Style::TabIndex; + using Rml::Style::TextAlign; + using Rml::Style::TextDecoration; + using Rml::Style::TextOverflow; + using Rml::Style::TextTransform; + using Rml::Style::Top; + using Rml::Style::TransformOrigin; + using Rml::Style::VerticalAlign; + using Rml::Style::Visibility; + using Rml::Style::WhiteSpace; + using Rml::Style::Width; + using Rml::Style::WordBreak; + using Rml::Style::ZIndex; + } + + namespace Transforms + { + using Rml::Transforms::DecomposedMatrix4; + using Rml::Transforms::Matrix2D; + using Rml::Transforms::Matrix3D; + using Rml::Transforms::Perspective; + using Rml::Transforms::ResolvedPrimitive; + using Rml::Transforms::Rotate2D; + using Rml::Transforms::Rotate3D; + using Rml::Transforms::RotateX; + using Rml::Transforms::RotateY; + using Rml::Transforms::RotateZ; + using Rml::Transforms::Scale2D; + using Rml::Transforms::Scale3D; + using Rml::Transforms::ScaleX; + using Rml::Transforms::ScaleY; + using Rml::Transforms::ScaleZ; + using Rml::Transforms::Skew2D; + using Rml::Transforms::SkewX; + using Rml::Transforms::SkewY; + using Rml::Transforms::Translate2D; + using Rml::Transforms::Translate3D; + using Rml::Transforms::TranslateX; + using Rml::Transforms::TranslateY; + using Rml::Transforms::TranslateZ; + using Rml::Transforms::UnresolvedPrimitive; + } + + namespace Utilities + { + using Rml::Utilities::HashCombine; + } + } + + using ::rmlui_dynamic_cast; + using ::rmlui_static_cast; + using ::rmlui_type_name; +} diff --git a/src/wrappers/rmlui/debugger.cppm b/src/wrappers/rmlui/debugger.cppm new file mode 100644 index 0000000..7dd319a --- /dev/null +++ b/src/wrappers/rmlui/debugger.cppm @@ -0,0 +1,14 @@ +module; + +#include + +export module wrappers.rmlui.debugger; + +export namespace Rml::Debugger +{ + using Rml::Debugger::Initialise; + using Rml::Debugger::IsVisible; + using Rml::Debugger::SetContext; + using Rml::Debugger::SetVisible; + using Rml::Debugger::Shutdown; +} diff --git a/tools/shuriken.py b/tools/shuriken.py index e5a1943..b7f38a7 100755 --- a/tools/shuriken.py +++ b/tools/shuriken.py @@ -10,7 +10,6 @@ ############################################ import argparse -import copy import json import shlex import subprocess @@ -18,7 +17,7 @@ import sys from collections import namedtuple from dataclasses import asdict, dataclass, field from pathlib import Path -from typing import Any +from typing import Any, Self import yaml @@ -60,6 +59,38 @@ class TargetConfig: return f"{self.linker_args or ''} {self.linker_args_extra or ''}".strip() +@dataclass(kw_only=True) +class MergedTargetConfig(TargetConfig): + cpp_compiler: str = '' + cpp_standard: str = '' + cpp_flags: str = '' + cpp_flags_extra: str = '' + + linker_flags: str = '' + linker_flags_extra: str = '' + linker_args: str = '' + linker_args_extra: str = '' + + output_file: str = '' + run_command: str = '' + + @classmethod + def create_from(cls, target_defaults: TargetConfig, target_config: TargetConfig) -> Self: + merged_config = cls() + merged_config.update(asdict(target_defaults)) + merged_config.update(asdict(target_config)) + return merged_config + + def validate(self) -> None: + # Validate that all required fields are set + if not self.cpp_compiler: + raise ConfigValidationException('cpp_compiler must be set') + if not self.cpp_standard: + raise ConfigValidationException('cpp_standard must be set') + if not self.output_file: + raise ConfigValidationException('output_file must be set') + + @dataclass(kw_only=True) class ShurikenConfig: __path_fields = ['source_dir', 'build_dir'] @@ -72,9 +103,6 @@ class ShurikenConfig: defaults: TargetConfig = field(default_factory=lambda: TargetConfig( cpp_compiler='clang++', cpp_standard='c++20', - cpp_flags='', - linker_flags='', - linker_args='', )) enable_import_std: bool = False @@ -82,6 +110,9 @@ class ShurikenConfig: default_target: str = '' targets: dict[str, TargetConfig] = field(default_factory=dict) + # Merged target configs, generated from defaults and targets + _merged_target_configs = {} + def update(self, data: dict) -> None: for key, value in data.items(): # Ignore unknown config keys @@ -116,6 +147,10 @@ class ShurikenConfig: else: setattr(self, key, str(value)) + # Generate merged target configs (we generate all of them so that we can validate the full config) + for target_name, target_config in self.targets.items(): + self._merged_target_configs[target_name] = MergedTargetConfig.create_from(self.defaults, target_config) + def update_from_yaml(self, file_path: Path) -> None: with file_path.open('r') as file: self.update(yaml.safe_load(file)) @@ -137,23 +172,18 @@ class ShurikenConfig: raise ConfigValidationException('default_target is not defined in targets') # Validate individual build targets - for target_name, target_config in self.targets.items(): - if not target_config.cpp_compiler and not self.defaults.cpp_compiler: - raise ConfigValidationException(f'Target "{target_name}": cpp_compiler must be set') - if not target_config.cpp_standard and not self.defaults.cpp_standard: - raise ConfigValidationException(f'Target "{target_name}": cpp_standard must be set') - if not target_config.output_file and not self.defaults.output_file: - raise ConfigValidationException(f'Target "{target_name}": output_file must be set') + for target_name, merged_target_config in self._merged_target_configs.items(): + try: + merged_target_config.validate() + except ConfigValidationException as exc: + raise ConfigValidationException(f'Target "{target_name}": {str(exc)}') def get_ninja_file_path(self, target: str) -> Path: return self.build_dir / f'build.{target}.ninja' - def get_merged_target_config(self, target: str) -> TargetConfig: + def get_merged_target_config(self, target: str) -> MergedTargetConfig: assert target in self.targets, f'Target "{target}" not defined!' - - merged_config = copy.deepcopy(self.defaults) - merged_config.update(asdict(self.targets[target])) - return merged_config + return self._merged_target_configs[target] class ShurikenArgumentParser: @@ -570,7 +600,25 @@ class NinjaFileGenerator: config = self.config target = self.target target_config = self.target_config - build_dir = self.config.build_dir + build_dir = config.build_dir + ninja_file_path = config.get_ninja_file_path(config.default_target) + cpp_compiler = target_config.cpp_compiler + + # Generate list of all source input files to be used as implicit dependencies for the dyndep file + dyndep_file_implicit_inputs = ' '.join(sorted( + t.src for t in self.compile_targets_cpp + self.compile_targets_cppm + )) + + # Generate list of all .dep files that are generated implicitly during dyndep file generation. + # This is mostly necessary so that Ninja can create the directories for these files beforehand. + dyndep_file_implicit_outputs = ' '.join(sorted( + f'{t.obj}.dep' for t in self.compile_targets_cpp + self.compile_targets_cppm + )) + + # Generate list of generated source files (currently just the copied std module file) + generated_source_files = ' '.join(sorted( + t.generated_src for t in self.compile_targets_std_module + )) # Generate Ninja file return f""" @@ -596,19 +644,21 @@ rule copy # Rule to compile a .cpp file to a .o file rule cpp - command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out + depfile = $out.dep + command = {cpp_compiler} $cpp_flags -MMD -MF $out.dep -c $in -o $out # Rule to compile a .cppm file (C++20 module) to a .o file ($out) and a .pcm file ($pcm_out) rule cppm - command = {target_config.cpp_compiler} $cpp_flags -c $in -o $out -fmodule-output=$pcm_out + depfile = $out.dep + command = {cpp_compiler} $cpp_flags -MMD -MF $out.dep -c $in -o $out -fmodule-output=$pcm_out # Rule to compile a standard library module to a .pcm file (e.g. "import std") rule std_module - command = {target_config.cpp_compiler} $cpp_std_module_flags --precompile $in -o $out + command = {cpp_compiler} $cpp_std_module_flags --precompile $in -o $out # Rule to link several .o files to an executable rule link - command = {target_config.cpp_compiler} $linker_flags -o $out $in $linker_args + command = {cpp_compiler} $linker_flags -o $out $in $linker_args # Rule to generate a compilation database (JSON file) from the build targets defined in a Ninja file rule generate_compdb @@ -622,13 +672,13 @@ rule generate_dyndeps # -- STATIC BUILD TARGETS # Generate compilation database from default target Ninja file -build $compdb_file: generate_compdb {self.config.get_ninja_file_path(config.default_target)} | generate_source_files +build $compdb_file: generate_compdb {ninja_file_path} | generate_source_files # Shortcut alias to generate compilation database build compdb: phony $compdb_file # Generate Ninja dyndep file from compilation database -build $dyndep_file: generate_dyndeps $compdb_file +build $dyndep_file | {dyndep_file_implicit_outputs}: generate_dyndeps $compdb_file | {dyndep_file_implicit_inputs} # -- GENERATED BUILD TARGETS @@ -638,17 +688,13 @@ build $builddir/{target_config.output_file}: link {' '.join(self.link_target_obj default $builddir/{target_config.output_file} # Phony target that depends on all generated source files -build generate_source_files: phony {self.list_generated_source_files()} +build generate_source_files: phony {generated_source_files} {self.render_compile_targets()} # End of build.{target}.ninja """.strip() - def list_generated_source_files(self) -> str: - generated_files = [target.generated_src for target in self.compile_targets_std_module] - return ' '.join(generated_files) - def render_compile_targets(self) -> str: rendered_targets = list(map(self.render_compile_target_std_module, self.compile_targets_std_module)) rendered_targets += list(map(self.render_compile_target_cpp, self.compile_targets_cpp))