Autogenerate TDLib .NET documentation.

GitOrigin-RevId: 9a73b2786306b8b2deaeb967aa4633d1d1ed0a2d
This commit is contained in:
levlam 2018-03-17 00:26:27 +03:00
parent 070f6ff184
commit 3856045a08
11 changed files with 272 additions and 36 deletions

View File

@ -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
```
<a name="using-json"></a>
## Using from other programming languages
`TDLib` provides efficient native C++, Java, and .NET interfaces.

View File

@ -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 <path to TDLib sources>/example/csharp
mkdir build

View File

@ -11,11 +11,11 @@ using TdApi = Telegram.Td.Api;
using System;
using System.Threading;
/**
* Example class for TDLib usage from C#.
*/
namespace TdExample
{
/// <summary>
/// Example class for TDLib usage from C#.
/// </summary>
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
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,223 @@
<?php
require_once 'TlDocumentationGenerator.php';
class DotnetTlDocumentationGenerator extends TlDocumentationGenerator
{
protected function escapeDocumentation($doc)
{
$doc = htmlspecialchars($doc, ENT_XML1);
$doc = str_replace('*/', '*&#47;', $doc);
$doc = preg_replace_callback('/_([A-Za-z])/', function ($matches) {return strtoupper($matches[1]);}, $doc);
return $doc;
}
protected function getFieldName($name, $class_name)
{
$name = ucfirst($this->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<byte>^';
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 {', <<<EOT
/// <summary>
/// This class is a base class for all TDLib interface classes.
/// </summary>
EOT
);
$this->addDocumentation(' virtual String^ ToString() override;', <<<EOT
/// <summary>
/// Returns string representation of the object.
/// </summary>
/// <returns>Returns string representation of the object.</returns>
EOT
);
$this->addDocumentation('public interface class Function : BaseObject {', <<<EOT
/// <summary>
/// This class is a base class for all TDLib interface function-classes.
/// </summary>
EOT
);
}
protected function addAbstractClassDocumentation($class_name, $documentation)
{
$this->addDocumentation("public interface class $class_name : Object {", <<<EOT
/// <summary>
/// This class is an abstract base class.
/// $documentation
/// </summary>
EOT
);
}
protected function addClassDocumentation($class_name, $base_class_name, $description, $return_type)
{
$return_type_description = $return_type ? "\r\n/// <para>Returns <see cref=\"".substr($return_type, 0, -1).'"/>.</para>' : '';
$this->addDocumentation("public ref class $class_name sealed : $base_class_name {", <<<EOT
/// <summary>
/// $description$return_type_description
/// </summary>
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, <<<EOT
/// <summary>
/// $field_info
/// </summary>
EOT
);
}
protected function addDefaultConstructorDocumentation($class_name)
{
$this->addDocumentation(" $class_name();", <<<EOT
/// <summary>
/// Default constructor.
/// </summary>
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 = <<<EOT
/// <summary>
/// Constructor for initialization of all fields.
/// </summary>
EOT;
foreach ($known_fields as $name => $type) {
$full_doc .= '\r\n /// <param name="'.$this->getParameterName($name, $class_name).'">'.$info[$name]."</param>";
}
$this->addDocumentation($full_constructor, $full_doc);
}
}
$generator = new DotnetTlDocumentationGenerator();
$generator->generate($argv[1], $argv[2]);

View File

@ -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 {", <<<EOT
/**
@ -352,11 +352,11 @@ EOT
protected function addFullConstructorDocumentation($class_name, $known_fields, $info)
{
$explicit = count($known_fields) == 1 ? 'explicit ' : '';
$explicit = count($known_fields) === 1 ? 'explicit ' : '';
$full_constructor = " $explicit$class_name(";
$colon = '';
foreach ($known_fields as $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);

View File

@ -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 * <p> Returns {@link $return_type $return_type} </p>" : '';
$return_type_description = $return_type ? PHP_EOL.' *'.PHP_EOL." * <p> Returns {@link $return_type $return_type} </p>" : '';
$this->addDocumentation(" public static class $class_name extends $base_class_name {", <<<EOT
/**
@ -208,7 +208,7 @@ EOT
EOT
);
if ($may_be_null && $this->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);

View File

@ -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));
}
}
}

View File

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

View File

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