From c1c1fd9f993ef1a1e560739d79d1190761206589 Mon Sep 17 00:00:00 2001 From: Jonitis Date: Mon, 8 Apr 2019 19:48:21 +0300 Subject: [PATCH] Fix indentation for idl output (Take 2) (#260) * Fix indentation for idl output Do not use hardcoded indentation for specific output types, since some of them are used in different contexts. For example custom attributes may be used for both classes and their methods. To use proper indentation use same approach as python tool that uses indent_guard class to automatically increase indentation in scope and specialize writer::write_impl() methods to automatically indent output for each line that starts on new line. * Move src/tool/idl to samples/meta_reader * Address review notes * Make meta_reader cmake file self-contained * add cmake policy statement for CMP0076 * added winml tutorial reference to samples/readme.md * modified CODEOWNERS to address moving IDL -> samples --- .github/CODEOWNERS | 4 +- samples/meta_reader/CMakeLists.txt | 60 ++++ .../tool/idl => samples/meta_reader}/main.cpp | 314 +++++++++++------- {src/tool/idl => samples/meta_reader}/pch.cpp | 0 {src/tool/idl => samples/meta_reader}/pch.h | 0 samples/readme.md | 7 + src/readme.md | 4 +- src/tool/CMakeLists.txt | 1 - src/tool/idl/CMakeLists.txt | 15 - 9 files changed, 268 insertions(+), 137 deletions(-) create mode 100644 samples/meta_reader/CMakeLists.txt rename {src/tool/idl => samples/meta_reader}/main.cpp (74%) rename {src/tool/idl => samples/meta_reader}/pch.cpp (100%) rename {src/tool/idl => samples/meta_reader}/pch.h (100%) create mode 100644 samples/readme.md delete mode 100644 src/tool/idl/CMakeLists.txt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d699266a..b9560ae6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -21,12 +21,12 @@ /src/test/cpp/ @kennykerr /src/tool/cpp/ @kennykerr -/src/tool/idl/ @kennykerr - /src/tool/natvis/ @Scottj1s /src/test/python/ @devhawk /src/tool/python/ @devhawk + +/samples @devhawk /samples/python/ @devhawk diff --git a/samples/meta_reader/CMakeLists.txt b/samples/meta_reader/CMakeLists.txt new file mode 100644 index 00000000..3bb8a737 --- /dev/null +++ b/samples/meta_reader/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_minimum_required(VERSION 3.9) + +if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.13") + cmake_policy(SET CMP0076 NEW) +endif() + +project(meta_reader) + +set(XLANG_LIBRARY_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../src/library") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(meta_reader "") +target_sources(meta_reader PUBLIC main.cpp pch.cpp) +target_include_directories(meta_reader PUBLIC ${XLANG_LIBRARY_PATH}) + +function(ADD_OBJECT_DEPENDS file dependency) + get_source_file_property(cur_obj_depends ${file} OBJECT_DEPENDS) + if (${cur_obj_depends} STREQUAL "NOTFOUND") + set(new_obj_depends "${pch_output_file_name}") + else() + set(new_obj_depends "${cur_obj_depends};${dependency}") + endif() + set_source_files_properties(${file} PROPERTIES OBJECT_DEPENDS ${new_obj_depends}) +endfunction(ADD_OBJECT_DEPENDS) + +function(TARGET_CONFIG_MSVC_PCH target pch_cpp pch_header) + get_target_property(target_name ${target} NAME) + set(pch_output_file_name "${target_name}.pch") + set(pch_output_file "\"${CMAKE_CURRENT_BINARY_DIR}/${pch_output_file_name}\"") + + get_target_property(sources ${target} SOURCES) + foreach(file ${sources}) + if (${file} STREQUAL ${pch_cpp}) + set_source_files_properties(${file} + PROPERTIES + COMPILE_FLAGS " /Yc${pch_header} /Fp${pch_output_file}" + OBJECT_OUTPUTS ${pch_output_file_name}) + else() + set_source_files_properties(${file} PROPERTIES COMPILE_FLAGS " /Yu${pch_header} /Fp${pch_output_file}") + ADD_OBJECT_DEPENDS(${file} ${pch_output_file_name}) + endif() + endforeach() + + set_target_properties(${target} PROPERTIES PCH_OUTPUT_FILE ${pch_output_file}) + set_property(GLOBAL APPEND PROPERTY PCH_OUTPUT_FILES ${pch_output_file}) +endfunction(TARGET_CONFIG_MSVC_PCH) + +if (WIN32) + add_definitions(-DNOMINMAX) +endif() + +if (MSVC) + TARGET_CONFIG_MSVC_PCH(meta_reader pch.cpp pch.h) + target_link_libraries(meta_reader) +else() + target_link_libraries(meta_reader c++ c++abi c++experimental) + target_link_libraries(meta_reader -lpthread) +endif() diff --git a/src/tool/idl/main.cpp b/samples/meta_reader/main.cpp similarity index 74% rename from src/tool/idl/main.cpp rename to samples/meta_reader/main.cpp index 2555f762..615784b7 100644 --- a/src/tool/idl/main.cpp +++ b/samples/meta_reader/main.cpp @@ -14,7 +14,26 @@ template overloaded(T...)->overloaded; struct writer : writer_base { using writer_base::write; + + struct indent_guard + { + explicit indent_guard(writer& w, int32_t offset = 1) noexcept : _writer(w), _offset(offset) + { + _writer.indent += _offset; + } + + ~indent_guard() + { + _writer.indent -= _offset; + } + + private: + writer& _writer; + int32_t _offset; + }; + std::string_view current; + int32_t indent{}; std::vector> generic_param_stack; @@ -47,6 +66,76 @@ struct writer : writer_base return generic_param_guard{ this }; } + void write_indent() + { + for (int32_t i = 0; i < indent; i++) + { + writer_base::write_impl(" "); + } + } + + void write_impl(std::string_view const& value) + { + std::string_view::size_type current_pos{ 0 }; + auto on_new_line = back() == '\n'; + + while (true) + { + const auto pos = value.find('\n', current_pos); + + if (pos == std::string_view::npos) + { + if (current_pos < value.size()) + { + if (on_new_line) + { + write_indent(); + } + + writer_base::write_impl(value.substr(current_pos)); + } + + return; + } + + auto current_line = value.substr(current_pos, pos - current_pos + 1); + auto empty_line = current_line[0] == '\n'; + + if (on_new_line && !empty_line) + { + write_indent(); + } + + writer_base::write_impl(current_line); + + on_new_line = true; + current_pos = pos + 1; + } + } + + void write_impl(char const value) + { + if (back() == '\n' && value != '\n') + { + write_indent(); + } + + writer_base::write_impl(value); + } + + template + std::string write_temp(std::string_view const& value, Args const& ... args) + { + auto restore_indent = indent; + indent = 0; + + auto result = writer_base::write_temp(value, args...); + + indent = restore_indent; + + return result; + } + void write_value(bool value) { write(value ? "TRUE"sv : "FALSE"sv); @@ -365,35 +454,35 @@ struct writer : writer_base { std::visit(overloaded{ [this](ElemSig::SystemType arg) - { - write(arg.name); - }, + { + write(arg.name); + }, [this](ElemSig::EnumValue arg) - { - auto const enumerators = find_enumerators(arg); - if (enumerators.empty()) { - std::visit([this](auto&& value) { write_value(value); }, arg.value); - } - else - { - bool first = true; - for (auto const& enumerator : enumerators) + auto const enumerators = find_enumerators(arg); + if (enumerators.empty()) { - if (!first) - { - write(" | "); - } - write("%.%.%", arg.type.m_typedef.TypeNamespace(), arg.type.m_typedef.TypeName(), enumerator.Name()); - first = false; + std::visit([this](auto&& value) { write_value(value); }, arg.value); } - } - }, + else + { + bool first = true; + for (auto const& enumerator : enumerators) + { + if (!first) + { + write(" | "); + } + write("%.%.%", arg.type.m_typedef.TypeNamespace(), arg.type.m_typedef.TypeName(), enumerator.Name()); + first = false; + } + } + }, [this](auto&& arg) - { - write_value(arg); - } - }, std::get(arg.value).value); + { + write_value(arg); + } + }, std::get(arg.value).value); } void write(NamedArgSig const& arg) @@ -405,19 +494,18 @@ struct writer : writer_base { auto const& args = arg.FixedArgs(); - write_printf("\n [Windows.Foundation.Metadata.GuidAttribute(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)]" - , std::get(std::get(args[0].value).value) - , std::get(std::get(args[1].value).value) - , std::get(std::get(args[2].value).value) - , std::get(std::get(args[3].value).value) - , std::get(std::get(args[4].value).value) - , std::get(std::get(args[5].value).value) - , std::get(std::get(args[6].value).value) - , std::get(std::get(args[7].value).value) - , std::get(std::get(args[8].value).value) - , std::get(std::get(args[9].value).value) - , std::get(std::get(args[10].value).value) - ); + write_printf("[Windows.Foundation.Metadata.GuidAttribute(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)]", + std::get(std::get(args[0].value).value), + std::get(std::get(args[1].value).value), + std::get(std::get(args[2].value).value), + std::get(std::get(args[3].value).value), + std::get(std::get(args[4].value).value), + std::get(std::get(args[5].value).value), + std::get(std::get(args[6].value).value), + std::get(std::get(args[7].value).value), + std::get(std::get(args[8].value).value), + std::get(std::get(args[9].value).value), + std::get(std::get(args[10].value).value)); } void write(CustomAttribute const& attr) @@ -431,7 +519,7 @@ struct writer : writer_base return; } - write("\n [%.%", name.first, name.second); + write("[%.%", name.first, name.second); bool first = true; for (auto const& fixed_arg : sig.FixedArgs()) @@ -469,6 +557,11 @@ struct writer : writer_base } }; +void write_custom_attribute(writer& w, CustomAttribute const& attr) +{ + w.write("\n%", attr); +} + void write_type_name(writer& w, std::string_view name) { w.write(name); @@ -493,41 +586,37 @@ void write_type_name(writer& w, std::string_view name) void write_enum_field(writer& w, Field const& field) { + writer::indent_guard _{ w }; + if (auto const& constant = field.Constant()) { - w.write("\n % = %,", - field.Name(), - constant); + w.write("\n% = %,", field.Name(), constant); } } void write_enum(writer& w, TypeDef const& type) { - for (auto const& attr : type.CustomAttribute()) - { - w.write(attr); - } + writer::indent_guard _{ w }; - w.write("\n enum %\n {%\n };\n", + w.write("%\nenum %\n{%\n};\n", + bind_each(type.CustomAttribute()), type.TypeName(), bind_each(type.FieldList())); } void write_struct_field(writer& w, Field const& field) { - w.write("\n % %;", - field.Signature().Type(), - field.Name()); + writer::indent_guard _{ w }; + + w.write("\n% %;", field.Signature().Type(), field.Name()); } void write_struct(writer& w, TypeDef const& type) { - for (auto const& attr : type.CustomAttribute()) - { - w.write(attr); - } + writer::indent_guard _{ w }; - w.write("\n struct %\n {%\n };\n", + w.write("%\nstruct %\n{%\n};\n", + bind_each(type.CustomAttribute()), type.TypeName(), bind_each(type.FieldList())); } @@ -536,6 +625,7 @@ void write_delegate(writer& w, TypeDef const& type) { auto guard{ w.push_generic_params(type.GenericParam()) }; auto methods = type.MethodList(); + writer::indent_guard _{ w }; auto method = std::find_if(begin(methods), end(methods), [](auto&& method) { @@ -547,22 +637,20 @@ void write_delegate(writer& w, TypeDef const& type) throw_invalid("Delegate's Invoke method not found"); } - for (auto const& attr : type.CustomAttribute()) - { - w.write(attr); - } - - w.write("\n delegate % %(%);\n", method.Signature().ReturnType(), bind(type.TypeName()), method); + w.write("%\ndelegate % %(%);\n", + bind_each(type.CustomAttribute()), + method.Signature().ReturnType(), + bind(type.TypeName()), + method); } void write_method(writer& w, MethodDef const& method) { - for (auto const& attr : method.CustomAttribute()) - { - w.write(attr); - } - - w.write("\n % %(%);", method.Signature().ReturnType(), method.Name(), method); + w.write("%\n% %(%);", + bind_each(method.CustomAttribute()), + method.Signature().ReturnType(), + method.Name(), + method); } void write_method_semantic(writer& w, MethodSemantics const& method_semantic, MethodDef& method) @@ -591,10 +679,7 @@ void write_method_semantic(writer& w, MethodSemantics const& method_semantic, Me if (method < other_method) { // First accessor seen for this property - for (auto const& attr : property.CustomAttribute()) - { - w.write(attr); - } + w.write(bind_each(property.CustomAttribute())); } if (semantic.Getter()) @@ -605,36 +690,34 @@ void write_method_semantic(writer& w, MethodSemantics const& method_semantic, Me { xlang::throw_invalid("Invalid semantic: properties can only have a setter and/or getter"); } - w.write("\n % %;", property.Type().Type(), property.Name()); + w.write("\n% %;", property.Type().Type(), property.Name()); ++method; } else { XLANG_ASSERT(semantic.Getter()); - w.write("\n % % { get; };", property.Type().Type(), property.Name()); + w.write("\n% % { get; };", property.Type().Type(), property.Name()); } } else { XLANG_ASSERT(semantic.Setter()); - w.write("\n % % { set; };", property.Type().Type(), property.Name()); + w.write("\n% % { set; };", property.Type().Type(), property.Name()); } } else { XLANG_ASSERT(distance(accessors) == 1); - for (auto const& attr : property.CustomAttribute()) - { - w.write(attr); - } + w.write(bind_each(property.CustomAttribute())); + if (semantic.Getter()) { - w.write("\n % % { get; };", property.Type().Type(), property.Name()); + w.write("\n% % { get; };", property.Type().Type(), property.Name()); } else { XLANG_ASSERT(semantic.Setter()); - w.write("\n % % { set; };", property.Type().Type(), property.Name()); + w.write("\n% % { set; };", property.Type().Type(), property.Name()); } } } @@ -659,10 +742,7 @@ void write_method_semantic(writer& w, MethodSemantics const& method_semantic, Me if (method < other_method) { // First accessor seen for this event - for (auto const& attr : event.CustomAttribute()) - { - w.write(attr); - } + w.write(bind_each(event.CustomAttribute())); } if (semantic.AddOn()) @@ -673,42 +753,47 @@ void write_method_semantic(writer& w, MethodSemantics const& method_semantic, Me { xlang::throw_invalid("Invalid semantic: events can only have a add and/or remove"); } - w.write("\n % %;", event.EventType(), event.Name()); + w.write("\n% %;", event.EventType(), event.Name()); ++method; } else { XLANG_ASSERT(semantic.AddOn()); - w.write("\n % % { add; };", event.EventType(), event.Name()); + w.write("\n% % { add; };", event.EventType(), event.Name()); } } else { XLANG_ASSERT(semantic.RemoveOn()); - w.write("\n % % { remove; };", event.EventType(), event.Name()); + w.write("\n% % { remove; };", event.EventType(), event.Name()); } } else { XLANG_ASSERT(distance(accessors) == 1); - for (auto const& attr : event.CustomAttribute()) - { - w.write(attr); - } + w.write(bind_each(event.CustomAttribute())); + if (semantic.AddOn()) { - w.write("\n % % { add; };", event.EventType(), event.Name()); + w.write("\n% % { add; };", event.EventType(), event.Name()); } else { XLANG_ASSERT(semantic.RemoveOn()); - w.write("\n % % { remove; };", event.EventType(), event.Name()); + w.write("\n% % { remove; };", event.EventType(), event.Name()); } } } } +void write_required_interface(writer& w, InterfaceImpl const& interface_impl) +{ + writer::indent_guard _{ w }; + + w.write("\n%", interface_impl.Interface()); +} + void write_required(writer& w, std::string_view const& requires, TypeDef const& type) { auto interfaces{ type.InterfaceImpl() }; @@ -718,7 +803,9 @@ void write_required(writer& w, std::string_view const& requires, TypeDef const& return; } - w.write(" %\n %", requires, bind_list(",\n ", interfaces)); + w.write(" %%", + requires, + bind_list(",", interfaces)); } void write_interface_methods(writer& w, TypeDef const& type) @@ -726,6 +813,7 @@ void write_interface_methods(writer& w, TypeDef const& type) auto const& methods = type.MethodList(); auto const& properties = type.PropertyList(); auto const& events = type.EventList(); + writer::indent_guard _{ w }; auto method_semantic = [&properties, &events](MethodDef const& method) -> MethodSemantics { @@ -769,13 +857,10 @@ void write_interface_methods(writer& w, TypeDef const& type) void write_interface(writer& w, TypeDef const& type) { auto guard = w.push_generic_params(type.GenericParam()); + writer::indent_guard _{ w }; - for (auto const& attr : type.CustomAttribute()) - { - w.write(attr); - } - - w.write("\n interface %%\n {%\n };\n", + w.write("%\ninterface %%\n{%\n};\n", + bind_each(type.CustomAttribute()), bind(type.TypeName()), bind("requires", type), bind(type)); @@ -784,13 +869,10 @@ void write_interface(writer& w, TypeDef const& type) void write_class(writer& w, TypeDef const& type) { auto guard = w.push_generic_params(type.GenericParam()); + writer::indent_guard _{ w }; - for (auto const& attr : type.CustomAttribute()) - { - w.write(attr); - } - - w.write("\n runtimeclass %%\n {\n };\n", + w.write("%\nruntimeclass %%\n{\n};\n", + bind_each(type.CustomAttribute()), bind(type.TypeName()), bind(":", type)); } @@ -853,28 +935,28 @@ int main(int const argc, char** argv) w.flush_to_console(); task_group group; - for (auto&& ns : c.namespaces()) + for (auto const& [ns, members] : c.namespaces()) { - group.add([&] + group.add([&, &ns = ns, &members = members] { - if (!f.includes(ns.second)) + if (!f.includes(members)) { return; } writer w; - w.current = ns.first; + w.current = ns; w.write("\nnamespace %\n{%%%%%}\n", - w.current, - f.bind_each(ns.second.enums), - f.bind_each(ns.second.structs), - f.bind_each(ns.second.delegates), - f.bind_each(ns.second.interfaces), - f.bind_each(ns.second.classes)); + ns, + f.bind_each(members.enums), + f.bind_each(members.structs), + f.bind_each(members.delegates), + f.bind_each(members.interfaces), + f.bind_each(members.classes)); auto filename{ out }; - filename += w.current; + filename += ns; filename += ".idl"; w.flush_to_file(filename); }); diff --git a/src/tool/idl/pch.cpp b/samples/meta_reader/pch.cpp similarity index 100% rename from src/tool/idl/pch.cpp rename to samples/meta_reader/pch.cpp diff --git a/src/tool/idl/pch.h b/samples/meta_reader/pch.h similarity index 100% rename from src/tool/idl/pch.h rename to samples/meta_reader/pch.h diff --git a/samples/readme.md b/samples/readme.md new file mode 100644 index 00000000..cbb27cfe --- /dev/null +++ b/samples/readme.md @@ -0,0 +1,7 @@ +# Samples + +## Folder Structure + +* **/meta_reader** sample metadata reader that generates [MIDL 3](http://docs.microsoft.com/en-us/uwp/midl-3/) files from ECMA-335 metadata. +* **/python/winml_tutorial** a port of the [WinML C++/WinRT tutorial](https://docs.microsoft.com/en-us/windows/ai/get-started-desktop) +to Python \ No newline at end of file diff --git a/src/readme.md b/src/readme.md index 6ae6f2ce..29bdaf16 100644 --- a/src/readme.md +++ b/src/readme.md @@ -114,9 +114,7 @@ Eventually, source in the PAL will be split into separate folders based on under The **/tool** folder contains the tools provided in support of xlang development. This includes tools for working with idl and winmd files as well as for generating language projections. -* **/tool/cpp** implements the tool that generates the C++ 17 language projection. Currently, the generated projection is Windows only, but the tool will be updated to generate cross-platform compatible code. - -* **/tool/idl** implements a tool that generates [MIDL 3](http://docs.microsoft.com/en-us/uwp/midl-3/) files from ECMA-335 metadata. +* **/tool/cppwinrt** implements the tool that generates the C++ 17 language projection. Currently, the generated projection is Windows only, but the tool will be updated to generate cross-platform compatible code. * **/tool/natviz** implements a [Natvis visualization](http://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects) of C++/WinRT components for the Visual Studio debugger 2017. Over time, this tool will be updated to support visualization of xlang components. diff --git a/src/tool/CMakeLists.txt b/src/tool/CMakeLists.txt index 690f2241..c0f298ee 100644 --- a/src/tool/CMakeLists.txt +++ b/src/tool/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.9) add_subdirectory(abi) -add_subdirectory(idl) add_subdirectory(python) add_subdirectory(cppxlang) diff --git a/src/tool/idl/CMakeLists.txt b/src/tool/idl/CMakeLists.txt deleted file mode 100644 index 10343301..00000000 --- a/src/tool/idl/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.9) - -project(idl) - -add_executable(idl "") -target_sources(idl PUBLIC main.cpp pch.cpp) -target_include_directories(idl PUBLIC ${XLANG_LIBRARY_PATH}) - -if (WIN32) - TARGET_CONFIG_MSVC_PCH(idl pch.cpp pch.h) - target_link_libraries(idl windowsapp ole32 shlwapi) -else() - target_link_libraries(idl c++ c++abi c++experimental) - target_link_libraries(idl -lpthread) -endif()