From 3856045a0816ea19ef88b9ed366ec0f3e0beaece Mon Sep 17 00:00:00 2001 From: levlam Date: Sat, 17 Mar 2018 00:26:27 +0300 Subject: [PATCH] Autogenerate TDLib .NET documentation. GitOrigin-RevId: 9a73b2786306b8b2deaeb967aa4633d1d1ed0a2d --- README.md | 5 + example/csharp/README.md | 2 +- example/csharp/TdExample.cs | 8 +- example/uwp/build.ps1 | 2 +- td/generate/CMakeLists.txt | 12 +- .../DotnetTlDocumentationGenerator.php | 223 ++++++++++++++++++ .../DoxygenTlDocumentationGenerator.php | 16 +- .../JavadocTlDocumentationGenerator.php | 15 +- td/generate/TlDocumentationGenerator.php | 13 +- td/generate/tl_writer_dotnet.h | 10 +- tdutils/td/utils/port/CxCli.h | 2 - 11 files changed, 272 insertions(+), 36 deletions(-) create mode 100644 td/generate/DotnetTlDocumentationGenerator.php diff --git a/README.md b/README.md index 57ba19f0..904715f4 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,11 @@ See [example/java](https://github.com/tdlib/td/tree/master/example/java) for exa See [example/csharp](https://github.com/tdlib/td/tree/master/example/csharp) for example of using `TDLib` from C# and detailed build and usage instructions. See [example/uwp](https://github.com/tdlib/td/tree/master/example/uwp) for example of using `TDLib` from C# UWP application and detailed build and usage instructions for Visual Studio Extension "TDLib for Universal Windows Platform". +When `TDLib` is built with `TD_ENABLE_DOTNET` option enabled, `C++` documentation is removed from some files. You need to checkout these files to return `C++` documentation back: +``` +git checkout td/telegram/Client.h td/telegram/Log.h td/tl/TlObject.h +``` + ## Using from other programming languages `TDLib` provides efficient native C++, Java, and .NET interfaces. diff --git a/example/csharp/README.md b/example/csharp/README.md index c503870a..b56cb52f 100644 --- a/example/csharp/README.md +++ b/example/csharp/README.md @@ -12,7 +12,7 @@ This is an example of building TDLib with `C++/CLI` support and an example of TD C:\src\vcpkg> .\vcpkg.exe install openssl:x64-windows openssl:x86-windows zlib:x64-windows zlib:x86-windows ``` * Download and install [gperf](https://sourceforge.net/projects/gnuwin32/files/gperf/3.0.1/). Add the path to gperf.exe to the PATH environment variable. -* Build `TDLib` with CMake enabling .NET support and specifying correct path to `vcpkg` toolchain file: +* Build `TDLib` with CMake enabling `.NET` support and specifying correct path to `vcpkg` toolchain file: ``` cd /example/csharp mkdir build diff --git a/example/csharp/TdExample.cs b/example/csharp/TdExample.cs index e1d2e3c6..c7823bef 100644 --- a/example/csharp/TdExample.cs +++ b/example/csharp/TdExample.cs @@ -11,11 +11,11 @@ using TdApi = Telegram.Td.Api; using System; using System.Threading; -/** - * Example class for TDLib usage from C#. - */ namespace TdExample { + /// + /// Example class for TDLib usage from C#. + /// class Example { private static Td.Client _client = null; @@ -256,7 +256,7 @@ namespace TdExample } else { - // ok result is already received through UpdateAuthorizationState, nothing to do + // result is already received through UpdateAuthorizationState, nothing to do } } } diff --git a/example/uwp/build.ps1 b/example/uwp/build.ps1 index 5ecb14f1..dfa21078 100644 --- a/example/uwp/build.ps1 +++ b/example/uwp/build.ps1 @@ -100,7 +100,7 @@ function export { cp ${arch}/Debug/* -filter "Telegram.Td.*" -include "*.pdb","*.dll" vsix/Redist/Debug/${arch}/ cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.pdb","*.dll" vsix/Redist/Retail/${arch}/ - cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.pri","*.winmd" vsix/References/CommonConfiguration/${arch}/ + cp ${arch}/Release/* -filter "Telegram.Td.*" -include "*.pri","*.winmd","*.xml" vsix/References/CommonConfiguration/${arch}/ } cd vsix diff --git a/td/generate/CMakeLists.txt b/td/generate/CMakeLists.txt index 78a4990d..0b07d616 100644 --- a/td/generate/CMakeLists.txt +++ b/td/generate/CMakeLists.txt @@ -98,7 +98,7 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${GENERATE_COMMON_CMD} COMMENT "Generate common tl source files" - DEPENDS generate_common scheme/mtproto_api.tlo scheme/telegram_api.tlo scheme/secret_api.tlo ${TL_TD_API_TLO} + DEPENDS generate_common scheme/mtproto_api.tlo scheme/telegram_api.tlo scheme/secret_api.tlo ${TL_TD_API_TLO} DoxygenTlDocumentationGenerator.php ) if (TD_ENABLE_JNI) target_compile_definitions(generate_common PRIVATE TD_ENABLE_JNI=1) @@ -135,13 +135,19 @@ if (NOT CMAKE_CROSSCOMPILING) endif() if (TD_ENABLE_DOTNET) + if (PHP_EXECUTABLE) + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TL_TD_API_TLO} && ${PHP_EXECUTABLE} DotnetTlDocumentationGenerator.php scheme/td_api.tl auto/td/telegram/TdDotNetApi.h) + else() + set(GENERATE_DOTNET_API_CMD td_generate_dotnet_api ${TL_TD_API_TLO}) + endif() + add_executable(td_generate_dotnet_api generate_dotnet.cpp tl_writer_dotnet.h) target_link_libraries(td_generate_dotnet_api PRIVATE tdtl) add_custom_target(generate_dotnet_api WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND td_generate_dotnet_api ${TL_TD_API_TLO} + COMMAND ${GENERATE_DOTNET_API_CMD} ${TL_TD_API_TLO} COMMENT "Generate .NET API files" - DEPENDS td_generate_dotnet_api ${TL_TD_API_TLO} + DEPENDS td_generate_dotnet_api ${TL_TD_API_TLO} DotnetTlDocumentationGenerator.php ) endif() diff --git a/td/generate/DotnetTlDocumentationGenerator.php b/td/generate/DotnetTlDocumentationGenerator.php new file mode 100644 index 00000000..7e11cc66 --- /dev/null +++ b/td/generate/DotnetTlDocumentationGenerator.php @@ -0,0 +1,223 @@ +getParameterName($name, $class_name)); + if ($name === $class_name) { + $name .= 'Value'; + } + return $name; + } + + protected function getParameterName($name, $class_name) + { + if (substr($name, 0, 6) === 'param_') { + $name = substr($name, 6); + } + $name = preg_replace_callback('/_([A-Za-z])/', function ($matches) {return strtoupper($matches[1]);}, trim($name)); + return $name; + } + + protected function getClassName($type) + { + return implode(array_map('ucfirst', explode('.', trim($type, "\r\n ;")))); + } + + protected function getTypeName($type) + { + switch ($type) { + case 'Bool': + return 'bool'; + case 'int32': + return 'int32'; + case 'int53': + case 'int64': + return 'int64'; + case 'double': + return 'float64'; + case 'string': + return 'String^'; + case 'bytes': + return 'Array^'; + case 'bool': + case 'int': + case 'long': + case 'Int': + case 'Long': + case 'Int32': + case 'Int53': + case 'Int64': + case 'Double': + case 'String': + case 'Bytes': + $this->printError("Wrong type $type"); + return ''; + default: + if (substr($type, 0, 6) === 'vector') { + if ($type[6] !== '<' || $type[strlen($type) - 1] !== '>') { + $this->printError("Wrong vector subtype in $type"); + return ''; + } + return 'Array<'.$this->getTypeName(substr($type, 7, -1)).'>^'; + } + + if (preg_match('/[^A-Za-z0-9.]/', $type)) { + $this->printError("Wrong type $type"); + return ''; + } + return $this->getClassName($type).'^'; + } + } + + protected function getBaseClassName($is_function) + { + return $is_function ? 'Function' : 'Object'; + } + + protected function needRemoveLine($line) + { + return strpos(trim($line), '///') === 0; + } + + protected function needSkipLine($line) + { + $line = trim($line); + return !$line || $line === 'public:' || $line === 'private:' || $line[0] === '}' || + strpos($line, 'Unmanaged') > 0 || strpos($line, 'PrivateField') > 0 || strpos($line, 'get()') > 0 || + strpos($line, 'void set(') === 0 || preg_match('/^[a-z]* class .*;/', $line) || + strpos($line, 'namespace ') === 0 || strpos($line, '#include ') === 0; + } + + protected function isHeaderLine($line) + { + return false; + } + + protected function extractClassName($line) + { + if (strpos($line, 'public ref class ') !== false || strpos($line, 'public interface class ') !== false) { + return explode(' ', $line)[3]; + } + return ''; + } + + protected function fixLine($line) + { + return $line; + } + + protected function addGlobalDocumentation() + { + $this->addDocumentation('public interface class Object : BaseObject {', << +/// This class is a base class for all TDLib interface classes. +/// +EOT +); + + $this->addDocumentation(' virtual String^ ToString() override;', << + /// Returns string representation of the object. + /// + /// Returns string representation of the object. +EOT +); + + $this->addDocumentation('public interface class Function : BaseObject {', << +/// This class is a base class for all TDLib interface function-classes. +/// +EOT +); + } + + protected function addAbstractClassDocumentation($class_name, $documentation) + { + $this->addDocumentation("public interface class $class_name : Object {", << +/// This class is an abstract base class. +/// $documentation +/// +EOT +); + } + + protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type) + { + $return_type_description = $return_type ? "\r\n/// Returns .' : ''; + + $this->addDocumentation("public ref class $class_name sealed : $base_class_name {", << +/// $description$return_type_description +/// +EOT +); + } + + protected function addFieldDocumentation($class_name, $field_name, $type_name, $field_info, $may_be_null) + { + $end = ';'; + if (substr($type_name, 0, strlen($field_name)) === $field_name) { + $type_name = '::Telegram::Td::Api::'.$type_name; + $end = ' {'; + } + $full_line = $class_name." property $type_name $field_name$end"; + $this->addDocumentation($full_line, << + /// $field_info + /// +EOT +); + } + + protected function addDefaultConstructorDocumentation($class_name) + { + $this->addDocumentation(" $class_name();", << + /// Default constructor. + /// +EOT +); + } + + protected function addFullConstructorDocumentation($class_name, $known_fields, $info) + { + $full_constructor = " $class_name("; + $colon = ''; + foreach ($known_fields as $name => $type) { + $field_type = $this->getTypeName($type); + if (substr($field_type, 0, 5) !== 'Array' && substr($field_type, 0, 6) !== 'String' && + ucfirst($field_type) === $field_type) { + $field_type = '::Telegram::Td::Api::'.$field_type; + } + $full_constructor .= $colon.$field_type.' '.$this->getParameterName($name, $class_name); + $colon = ', '; + } + $full_constructor .= ');'; + + $full_doc = << + /// Constructor for initialization of all fields. + /// +EOT; + foreach ($known_fields as $name => $type) { + $full_doc .= '\r\n /// '.$info[$name].""; + } + $this->addDocumentation($full_constructor, $full_doc); + } +} + +$generator = new DotnetTlDocumentationGenerator(); +$generator->generate($argv[1], $argv[2]); diff --git a/td/generate/DoxygenTlDocumentationGenerator.php b/td/generate/DoxygenTlDocumentationGenerator.php index 11cefcbc..377e3b37 100644 --- a/td/generate/DoxygenTlDocumentationGenerator.php +++ b/td/generate/DoxygenTlDocumentationGenerator.php @@ -43,7 +43,7 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator return $doc; } - protected function getFieldName($name) + protected function getFieldName($name, $class_name) { if (substr($name, 0, 6) === 'param_') { $name = substr($name, 6); @@ -53,7 +53,7 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator protected function getClassName($type) { - return implode(explode('.', trim($type, "\n ;"))); + return implode(explode('.', trim($type, "\r\n ;"))); } protected function getTypeName($type) @@ -115,11 +115,11 @@ class DoxygenTlDocumentationGenerator extends TlDocumentationGenerator protected function needSkipLine($line) { $tline = trim($line); - return empty($tline) || $tline[0] == '}' || $tline == 'public:' || strpos($line, '#pragma ') === 0 || + return empty($tline) || $tline[0] === '}' || $tline === 'public:' || strpos($line, '#pragma ') === 0 || strpos($line, '#include <') === 0 || strpos($tline, 'return ') === 0 || strpos($tline, 'namespace') === 0 || preg_match('/class [A-Za-z0-9_]*;/', $line) || $tline === 'if (value == nullptr) {' || strpos($line, 'JNIEnv') || strpos($line, 'jfieldID') || $tline === 'virtual ~Object() {' || - $tline == 'virtual void store(TlStorerToString &s, const char *field_name) const = 0;'; + $tline === 'virtual void store(TlStorerToString &s, const char *field_name) const = 0;'; } protected function isHeaderLine($line) @@ -322,7 +322,7 @@ EOT protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type) { - $return_type_description = $return_type ? "\n *\n * Returns $return_type." : ''; + $return_type_description = $return_type ? PHP_EOL.' *'.PHP_EOL." * Returns $return_type." : ''; $this->addDocumentation("class $class_name final : public $base_class_name {", << $type) { - $full_constructor .= $colon.$this->getParameterTypeName($type).$this->getFieldName($name); + $full_constructor .= $colon.$this->getParameterTypeName($type).$this->getFieldName($name, $class_name); $colon = ', '; } $full_constructor .= ');'; @@ -368,7 +368,7 @@ EOT EOT; foreach ($known_fields as $name => $type) { - $full_doc .= ' * \\param[in] '.$this->getFieldName($name).' '.$info[$name]."\n"; + $full_doc .= ' * \\param[in] '.$this->getFieldName($name, $class_name).' '.$info[$name].PHP_EOL; } $full_doc .= ' */'; $this->addDocumentation($full_constructor, $full_doc); diff --git a/td/generate/JavadocTlDocumentationGenerator.php b/td/generate/JavadocTlDocumentationGenerator.php index a0dd53f3..6cf17069 100644 --- a/td/generate/JavadocTlDocumentationGenerator.php +++ b/td/generate/JavadocTlDocumentationGenerator.php @@ -16,7 +16,7 @@ class JavadocTlDocumentationGenerator extends TlDocumentationGenerator return $doc; } - protected function getFieldName($name) + protected function getFieldName($name, $class_name) { if (substr($name, 0, 6) === 'param_') { $name = substr($name, 6); @@ -26,7 +26,7 @@ class JavadocTlDocumentationGenerator extends TlDocumentationGenerator protected function getClassName($type) { - return implode(array_map('ucfirst', explode('.', trim($type, "\n ;")))); + return implode(array_map('ucfirst', explode('.', trim($type, "\r\n ;")))); } protected function getTypeName($type) @@ -117,7 +117,7 @@ class JavadocTlDocumentationGenerator extends TlDocumentationGenerator protected function addGlobalDocumentation() { if ($this->nullable_type) { - $nullable_type_import = "import $this->nullable_type;\n"; + $nullable_type_import = "import $this->nullable_type;".PHP_EOL; } else { $nullable_type_import = ''; } @@ -188,7 +188,7 @@ EOT protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type) { - $return_type_description = $return_type ? "\n *\n *

Returns {@link $return_type $return_type}

" : ''; + $return_type_description = $return_type ? PHP_EOL.' *'.PHP_EOL." *

Returns {@link $return_type $return_type}

" : ''; $this->addDocumentation(" public static class $class_name extends $base_class_name {", <<nullable_annotation && ($this->java_version >= 8 || substr($type_name, -1) != ']')) { - $this->addLineReplacement($full_line, " public $this->nullable_annotation $type_name $field_name;\n"); + $this->addLineReplacement($full_line, " public $this->nullable_annotation $type_name $field_name;".PHP_EOL); } } @@ -224,11 +224,10 @@ EOT protected function addFullConstructorDocumentation($class_name, $known_fields, $info) { - $explicit = count($known_fields) == 1 ? 'explicit ' : ''; $full_constructor = " public $class_name("; $colon = ''; foreach ($known_fields as $name => $type) { - $full_constructor .= $colon.$this->getTypeName($type).' '.$this->getFieldName($name); + $full_constructor .= $colon.$this->getTypeName($type).' '.$this->getFieldName($name, $class_name); $colon = ', '; } $full_constructor .= ') {'; @@ -240,7 +239,7 @@ EOT EOT; foreach ($known_fields as $name => $type) { - $full_doc .= ' * @param '.$this->getFieldName($name).' '.$info[$name]."\n"; + $full_doc .= ' * @param '.$this->getFieldName($name, $class_name).' '.$info[$name].PHP_EOL; } $full_doc .= ' */'; $this->addDocumentation($full_constructor, $full_doc); diff --git a/td/generate/TlDocumentationGenerator.php b/td/generate/TlDocumentationGenerator.php index 31db40d8..11a281ce 100644 --- a/td/generate/TlDocumentationGenerator.php +++ b/td/generate/TlDocumentationGenerator.php @@ -17,6 +17,7 @@ abstract class TlDocumentationGenerator } $this->documentation[$code] = $doc; + // $this->printError($code); } final protected function addLineReplacement($line, $new_line) { @@ -64,7 +65,7 @@ abstract class TlDocumentationGenerator abstract protected function escapeDocumentation($doc); - abstract protected function getFieldName($name); + abstract protected function getFieldName($name, $class_name); abstract protected function getClassName($name); @@ -187,7 +188,7 @@ abstract class TlDocumentationGenerator if (!$is_function) { $type_lower = strtolower($type); $class_name_lower = strtolower($class_name); - if (empty($current_class) == ($type_lower !== $class_name_lower)) { + if (empty($current_class) === ($type_lower !== $class_name_lower)) { $this->printError('Wrong constructor name'); } if (strpos($class_name_lower, $type_lower) !== 0) { @@ -236,7 +237,7 @@ abstract class TlDocumentationGenerator foreach ($known_fields as $name => $type) { $may_be_null = stripos($info[$name], 'may be null') !== false; - $this->addFieldDocumentation($class_name, $this->getFieldName($name), $this->getTypeName($type), $info[$name], $may_be_null); + $this->addFieldDocumentation($class_name, $this->getFieldName($name, $class_name), $this->getTypeName($type), $info[$name], $may_be_null); } $this->addDefaultConstructorDocumentation($class_name); @@ -275,8 +276,10 @@ abstract class TlDocumentationGenerator $doc = ''; if (isset($this->documentation[$fixed_line])) { $doc = $this->documentation[$fixed_line]; + // unset($this->documentation[$fixed_line]); } elseif (isset($this->documentation[$current_class.$fixed_line])) { $doc = $this->documentation[$current_class.$fixed_line]; + // unset($this->documentation[$current_class.$fixed_line]); } else { $this->printError('Have no docs for "'.$fixed_line.'"'); } @@ -295,5 +298,9 @@ abstract class TlDocumentationGenerator if (file_get_contents($source_file) !== $result) { file_put_contents($source_file, $result); } + + if (count($this->documentation)) { + // $this->printError('Have unused docs '.print_r(array_keys($this->documentation), true)); + } } } diff --git a/td/generate/tl_writer_dotnet.h b/td/generate/tl_writer_dotnet.h index 976b93f4..8228fa23 100644 --- a/td/generate/tl_writer_dotnet.h +++ b/td/generate/tl_writer_dotnet.h @@ -228,14 +228,14 @@ class TlWriterDotNet : public TL_writer { auto fixed_type_name = "::Telegram::Td::Api::" + type_name; std::stringstream ss; ss << "private:\n"; - ss << " " << fixed_type_name << " " << fixed_field_name << "Private;\n"; + ss << " " << fixed_type_name << " " << fixed_field_name << "PrivateField;\n"; ss << "public:\n"; ss << " property " << fixed_type_name << " " << fixed_field_name << " {\n"; ss << " " << fixed_type_name << " get() {\n"; - ss << " return " << fixed_field_name << "Private;\n"; + ss << " return " << fixed_field_name << "PrivateField;\n"; ss << " }\n"; ss << " void set(" << fixed_type_name << " newValue) {\n"; - ss << " " << fixed_field_name << "Private = newValue;\n"; + ss << " " << fixed_field_name << "PrivateField = newValue;\n"; ss << " }\n"; ss << " }\n"; return ss.str(); @@ -281,9 +281,7 @@ class TlWriterDotNet : public TL_writer { std::stringstream ss; ss << (field_num == 0 ? "" : ", "); auto field_type = gen_field_type(a); - if (field_type.substr(0, 5) == "Array") { - ss << "CXCONST "; - } else if (field_type.substr(0, 6) != "String" && to_upper(field_type[0]) == field_type[0]) { + if (field_type.substr(0, 5) != "Array" && field_type.substr(0, 6) != "String" && to_upper(field_type[0]) == field_type[0]) { field_type = "::Telegram::Td::Api::" + field_type; } ss << field_type << " " << to_camelCase(a.name); diff --git a/tdutils/td/utils/port/CxCli.h b/tdutils/td/utils/port/CxCli.h index ea7c5282..b7f01fa4 100644 --- a/tdutils/td/utils/port/CxCli.h +++ b/tdutils/td/utils/port/CxCli.h @@ -23,7 +23,6 @@ #define REF_NEW ref new #define CLRCALL -#define CXCONST namespace CxCli { @@ -93,7 +92,6 @@ inline String^ string_from_unmanaged(const std::string &from) { #define REF_NEW gcnew #define CLRCALL __clrcall -#define CXCONST namespace CxCli {