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
This commit is contained in:
Jonitis 2019-04-08 19:48:21 +03:00 коммит произвёл Harry Pierson
Родитель 16bc9886fa
Коммит c1c1fd9f99
9 изменённых файлов: 268 добавлений и 137 удалений

4
.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

Просмотреть файл

@ -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()

Просмотреть файл

@ -14,7 +14,26 @@ template <typename...T> overloaded(T...)->overloaded<T...>;
struct writer : writer_base<writer>
{
using writer_base<writer>::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<std::pair<GenericParam, GenericParam>> generic_param_stack;
@ -47,6 +66,76 @@ struct writer : writer_base<writer>
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 <typename... Args>
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<writer>
{
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<ElemSig>(arg.value).value);
{
write_value(arg);
}
}, std::get<ElemSig>(arg.value).value);
}
void write(NamedArgSig const& arg)
@ -405,19 +494,18 @@ struct writer : writer_base<writer>
{
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<uint32_t>(std::get<ElemSig>(args[0].value).value)
, std::get<uint16_t>(std::get<ElemSig>(args[1].value).value)
, std::get<uint16_t>(std::get<ElemSig>(args[2].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[3].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[4].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[5].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[6].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[7].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[8].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[9].value).value)
, std::get<uint8_t>(std::get<ElemSig>(args[10].value).value)
);
write_printf("[Windows.Foundation.Metadata.GuidAttribute(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)]",
std::get<uint32_t>(std::get<ElemSig>(args[0].value).value),
std::get<uint16_t>(std::get<ElemSig>(args[1].value).value),
std::get<uint16_t>(std::get<ElemSig>(args[2].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[3].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[4].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[5].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[6].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[7].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[8].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[9].value).value),
std::get<uint8_t>(std::get<ElemSig>(args[10].value).value));
}
void write(CustomAttribute const& attr)
@ -431,7 +519,7 @@ struct writer : writer_base<writer>
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<writer>
}
};
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<write_custom_attribute>(type.CustomAttribute()),
type.TypeName(),
bind_each<write_enum_field>(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<write_custom_attribute>(type.CustomAttribute()),
type.TypeName(),
bind_each<write_struct_field>(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<write_type_name>(type.TypeName()), method);
w.write("%\ndelegate % %(%);\n",
bind_each<write_custom_attribute>(type.CustomAttribute()),
method.Signature().ReturnType(),
bind<write_type_name>(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<write_custom_attribute>(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<write_custom_attribute>(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<write_custom_attribute>(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<write_custom_attribute>(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<write_custom_attribute>(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<write_required_interface>(",", 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<write_custom_attribute>(type.CustomAttribute()),
bind<write_type_name>(type.TypeName()),
bind<write_required>("requires", type),
bind<write_interface_methods>(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<write_custom_attribute>(type.CustomAttribute()),
bind<write_type_name>(type.TypeName()),
bind<write_required>(":", 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<write_enum>(ns.second.enums),
f.bind_each<write_struct>(ns.second.structs),
f.bind_each<write_delegate>(ns.second.delegates),
f.bind_each<write_interface>(ns.second.interfaces),
f.bind_each<write_class>(ns.second.classes));
ns,
f.bind_each<write_enum>(members.enums),
f.bind_each<write_struct>(members.structs),
f.bind_each<write_delegate>(members.delegates),
f.bind_each<write_interface>(members.interfaces),
f.bind_each<write_class>(members.classes));
auto filename{ out };
filename += w.current;
filename += ns;
filename += ".idl";
w.flush_to_file(filename);
});

Просмотреть файл

Просмотреть файл

7
samples/readme.md Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -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.

Просмотреть файл

@ -1,7 +1,6 @@
cmake_minimum_required(VERSION 3.9)
add_subdirectory(abi)
add_subdirectory(idl)
add_subdirectory(python)
add_subdirectory(cppxlang)

Просмотреть файл

@ -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()