Skip to content
Commits on Source (4)
...@@ -6,3 +6,7 @@ RPCs) compatible with the [Mercury](https://mercury-hpc.github.io/) framework. ...@@ -6,3 +6,7 @@ RPCs) compatible with the [Mercury](https://mercury-hpc.github.io/) framework.
Inspired by Google's [Protocol Buffers](https://developers.google.com/protocol-buffers), **rpcc** Inspired by Google's [Protocol Buffers](https://developers.google.com/protocol-buffers), **rpcc**
allows developers to easily define RPCs using a language- and platform- neutral language, that will then be used to allows developers to easily define RPCs using a language- and platform- neutral language, that will then be used to
generate all the necessary C/C++ boilerplate code required to actually implement them. generate all the necessary C/C++ boilerplate code required to actually implement them.
## Documentation
Documentation for the compiler can be found [here](https://storage.bsc.es/projects/rpcc/).
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
SPHINXMV = sphinx-multiversion
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
versions:
$(SPHINXMV) "$(SOURCEDIR)" "$(BUILDDIR)/html"
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
from pygments.lexer import ExtendedRegexLexer, RegexLexer, bygroups, words, \
include, default, this, using, combined
from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
Number, Punctuation, Literal, Whitespace
from sphinx.highlighting import lexers
class RpccLexer(RegexLexer):
"""
Lexer for rpcc definition files.
"""
name = 'rpcc'
aliases = ['rpcc']
filenames = ['*.rpcc']
tokens = {
'root': [
(r'[ \t]+', Text),
(r'[,;{}\[\]()<>]', Punctuation),
(r'#.*\n', Comment.Single),
(words((
'rpc', 'arguments', 'return_values'), prefix=r'\b', suffix=r'\b'),
Keyword),
include('builtins'),
(words((
'int8', 'int16', 'int32', 'int64',
'uint8', 'uint16', 'uint32', 'uint64',
'float', 'double', 'bool', 'string', 'exposed_buffer'), suffix=r'\b'),
Keyword.Type),
(r'(true|false)\b', Keyword.Constant),
(r'(package)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Text), 'package'),
(r'(rpc)(\s+)',
bygroups(Keyword.Declaration, Text), 'rpc'),
(r'\".*?\"', String),
(r'\'.*?\'', String),
(r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[LlUu]*', Number.Float),
(r'(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float),
(r'(\-?(inf|nan))\b', Number.Float),
(r'0x[0-9a-fA-F]+[LlUu]*', Number.Hex),
(r'0[0-7]+[LlUu]*', Number.Oct),
(r'\d+[LlUu]*', Number.Integer),
(r'[+-=:]', Operator),
(r'([a-zA-Z_][\w.]*)([ \t]*)(=)',
bygroups(Name.Attribute, Text, Operator)),
(r'[a-zA-Z_][\w.]*', Name),
],
'package': [
(r'(?:[ \t]|\\\n)+', Text),
(r'as\b', Keyword.Namespace),
(r',', Operator),
(r'[a-zA-Z_][\w.]*', Name.Namespace),
default('#pop') # all else: go back
],
'rpc': [
(r'[a-zA-Z_]\w*', Name.Class, '#pop'),
default('#pop'),
],
'builtins': [
(words((
'id', 'include_if'), prefix=r'(?<!\.)', suffix=r'\b'), Name.Function.Magic),
(words((
'compiler_defines', 'compiler_not_defines'), prefix=r'(?<!\.)', suffix=r'\b'), Name.Attribute)
]
}
def setup(app):
app.add_lexer('rpcc', RpccLexer)
{%- if current_version %}
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
<span class="rst-current-version" data-toggle="rst-current-version">
<span class="fa fa-book"> Other Versions</span>
v: {{ current_version.name }}
<span class="fa fa-caret-down"></span>
</span>
<div class="rst-other-versions">
{%- if versions.tags %}
<dl>
<dt>Tags</dt>
{%- for item in versions.tags %}
<dd><a href="{{ item.url }}">{{ item.name }}</a></dd>
{%- endfor %}
</dl>
{%- endif %}
{%- if versions.branches %}
<dl>
<dt>Branches</dt>
{%- for item in versions.branches %}
<dd><a href="{{ item.url }}">{{ item.name }}</a></dd>
{%- endfor %}
</dl>
{%- endif %}
</div>
</div>
{%- endif %}
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
import pkg_resources
sys.path.insert(0, os.path.abspath('../..'))
sys.path.append(os.path.abspath("./_ext"))
# -- Project information -----------------------------------------------------
project = 'rpcc'
copyright = '2022, Barcelona Supercomputing Center'
author = 'Alberto Miranda'
# The full version, including alpha/beta/rc tags
release = pkg_resources.get_distribution("rpcc").version
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.autosectionlabel',
'sphinx_copybutton',
'sphinx_multiversion',
'rpcc_lexer'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
rpcc
####
.. toctree::
:maxdepth: 2
:hidden:
quickstart
.. toctree::
:maxdepth: 2
:hidden:
language
.. toctree::
:maxdepth: 2
:hidden:
libraries
:code:`rpcc` is a Python command line tool that allows developers to easily define
and work with remote procedure calls (RPCs) compatible with the `Mercury
<https://mercury-hpc.github.io/>`_ framework.
Inspired by Google's
`Protocol Buffers <https://developers.google.com/protocol-buffers>`_, :code:`rpcc`
allows developers to easily define RPCs using a language- and platform- neutral
language, that will then be used to generate all the necessary C/C++
boilerplate code required to actually implement them.
.. note::
This project is under active development.
.. _intro_installation:
Installation
------------
You can install :code:`rpcc` from PyPI with *pip* or your favorite package manager:
.. code-block:: console
$ pip install rpcc
If **rpcc** is already installed in your system, add the ``-U`` switch to
update to the current version.
.. warning::
:code:`rpcc` requires Python 3.6 and above.
Language Guide
==============
This reference guide describes how to use the rpcc language to structure your remote procedure's data.
.. _language_data_types:
Data types
----------
A :code:`rpcc` message field can have on of the following types:
.. list-table::
:widths: 20 40 40
:header-rows: 1
* - .rpcc type
- Mercury type
- C++ type
* - bool
- hg_bool_t
- bool
* - int8
- hg_int8_t
- int8_t
* - int16
- hg_int16_t
- int16_t
* - int32
- hg_int32_t
- int32_t
* - int64
- hg_int64_t
- int64_t
* - uint8
- hg_uint8_t
- uint8_t
* - uint16
- hg_uint16_t
- uint16_t
* - uint32
- hg_uint32_t
- uint32_t
* - uint64
- hg_uint64_t
- uint64_t
* - csize
- hg_size_t
- size_t
* - float
- float
- float
* - double
- double
- double
* - string
- hg_const_string_t
- std::string
* - exposed_buffer
- hg_bulk_t
- hermes::exposed_buffer
.. _language_rpc_options:
RPC options
-----------
Individual rpc declarations in a :code:`.rpcc` file can be annotated with several *options* that control how the
resulting code should be generated. The following options can be provided:
- :code:`id`: The identifier associated to this remote procedure. The value provided must be unique, otherwise
compilation will fail. If unspecified, :code:`rpcc` will internally generate a unique identifier for the rpc.
.. code-block:: rpcc
rpc foo {
id: 42;
arguments {
int32 bar;
};
return_values {
bool success;
};
};
- :code:`include_if`: This option allows developers to conditionally include rpc definitions in the generated code. Any
such rpc will be enclosed between appropriate C/C++ *preprocessor tags* so that the build system can control
whether a rpc definition should be made available to user code.
.. code-block:: rpcc
rpc foo {
# the classes for this RPC will be enclosed between #ifdef and #endif clauses
# in the generated code
include_if: compiler_defines(HAS_FOO);
arguments {
int32 bar;
};
return_values {
bool success;
};
};
rpc bar {
# the classes for this RPC will be enclosed between #ifndef and #endif clauses
# in the generated code
include_if: compiler_not_defines(HAS_BAR);
arguments {
int32 baz;
};
return_values {
bool success;
};
};
Supported Libraries
===================
The :code:`rpcc` compiler is capable of generating classes/source code so that it can be used with the following
Mercury-compatible libraries:
+-----------------+----------+----------------------+----------+
| Backend library | Language | Supported standards | Status |
+=================+==========+======================+==========+
| Hermes | C++ | C++17 | Complete |
+-----------------+----------+----------------------+----------+
| Mercury | C | C99 | Pending |
+-----------------+----------+----------------------+----------+
| Margo | C | C99 | Pending |
+-----------------+----------+----------------------+----------+
Quickstart
==========
This tutorial provides a basic programmer's introduction to working with rpcc to define
RPCs for the Mercury framework. By following this tutorial, we will be able to:
1. Define rpcs in a :code:`.rpcc` file.
2. Use the :code:`rpcc` compiler to generate C/C++ source files.
Defining our RPCs
------------------
In order to create Mercury RPCs for our service, we need to start with a :code:`.rpcc` file. The
definitions in a :code:`.rpcc` file are simple: we simply add a :code:`rpc` statement for each remote procedure we
need, and then define both its :code:`arguments` and :code:`return_values` by specifying the
:ref:`types <language_data_types>` and names of the fields that compose each message.
Here's an example of a simple remote procedure definition:
.. code-block:: rpcc
# contents of rpc_sample.rpcc
package tutorial;
rpc hello_world {
id: 42;
arguments {
string message;
};
return_values {
int8 error_code;
};
};
In this example, our service provides a simple :code:`hello_world` remote procedure that requires a string with the
containing the message that should be printed, and returns an :code:`error_code` to let the developer know whether
the remote procedure executed successfully or not. As we can see, the syntax of a :code:`.rpcc` file is pretty similar to C++ or Java. Let's go through each part of the
file and see what it does:
- The file starts with a :code:`package` declaration, which helps prevent naming conflicts when integrating the
generated code into an existing project. In C++, all the generated classes and functions will be placed in a namespace
matching the package name.
- Next, we have the remote procedure definitions. A remote procedure definition is just a *named* structure that
contains the defined :code:`arguments` and :code:`return_values` of the rpc, alongside some
:ref:`options <language_rpc_options>` that control how the rpc itself is generated.
.. note::
Note that, in Mercury, each rpc requires a unique identifier so that the corresponding functions can be
executed on the target. By default, the :code:`rpcc` compiler will automically generate unique identifiers for each
defined rpc. The :code:`id` option shown in the example can be used to override this behavior, allowing developers
to explictly provide a user-defined unique identifier for the rpc.
Compiling our remote procedures
--------------------------------
Now that we have a :code:`.rpcc` file, the next thing we need is to generate the code that we will use to be able to
call the :code:`hello_world` remote procedure. To do this, we need to run the :code:`rpcc` compiler on our
:code:`sample_rpc.rpcc` file.
1. If you haven't installed the compiler, follow :ref:`these instructions <intro_installation>` to install the latest
version.
2. Now we run the compiler, specifying the desired *output mode* (i.e. which of the
:ref:`Supported Libraries` is going to be used by client code to interact with remote procedures), as well as the
*code standard* to use when generating code.
.. code-block:: console
$ rpcc --output-mode=hermes --std=c++17 rpc_sample.rpcc
.. warning::
Note that some of the supported output modes may not work with all the code standards.
This generates the following Hermes-compatible source files in the current directory, which should
be integrated into your project's build system:
- :code:`rpc_sample-rpcc.hpp`, the header which declares our generated classes.
- :code:`rpc_sample-rpcc.cpp`, which contains the implementation of our classes.
alabaster==0.7.12
attrs==21.2.0 attrs==21.2.0
Babel==2.9.1
certifi==2021.10.8
charset-normalizer==2.0.9
colorama==0.4.4
commonmark==0.9.1
docutils==0.17.1
idna==3.3
imagesize==1.3.0
inflection==0.5.1
iniconfig==1.1.1 iniconfig==1.1.1
Jinja2==3.0.3
lark==1.0.0 lark==1.0.0
loguru==0.5.3 loguru==0.5.3
MarkupSafe==2.0.1
packaging==21.3 packaging==21.3
pluggy==1.0.0 pluggy==1.0.0
py==1.11.0 py==1.11.0
Pygments==2.10.0
pyparsing==3.0.6 pyparsing==3.0.6
pytest==6.2.5 pytest==6.2.5
pytest-mock==3.6.1
pytz==2021.3
requests==2.26.0
rich==10.15.1
snowballstemmer==2.2.0
Sphinx==4.3.1
sphinx-copybutton==0.4.0
sphinx-multiversion==0.2.4
sphinx-rtd-theme==1.0.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
toml==0.10.2 toml==0.10.2
rich~=10.15.1 typing_extensions==4.0.1
setuptools~=58.3.0 urllib3==1.26.7
import pathlib
from setuptools import setup from setuptools import setup
from rpcc.version import __version__ from rpcc.version import __version__
# package metadata
NAME = "rpcc"
DESCRIPTION = "A compiler for Mercury-based RPCs"
URL = "https://storage.bsc.es/projects/rpcc"
AUTHOR = "Alberto Miranda"
EMAIL = "alberto.miranda@bsc.es"
# The directory containing this file
HERE = pathlib.Path(__file__).parent
# The text of the README file
README = (HERE / "README.md").read_text()
setup( setup(
name="rpcc", name=NAME,
version=__version__, version=__version__,
packages=["rpcc"], description=DESCRIPTION,
author="Alberto Miranda", long_description=README,
author_email="alberto.miranda@bsc.es", long_description_content_type="text/markdown",
url=URL,
author=AUTHOR,
author_email=EMAIL,
license="GPLv3", license="GPLv3",
python_requires='>=3.6.0',
packages=["rpcc"],
include_package_data=True,
classifiers=[
# Trove classifiers
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)"
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development :: Code Generators"
],
entry_points={ entry_points={
"console_scripts": [ "console_scripts": [
"rpcc = rpcc.__main__:main" "rpcc = rpcc.__main__:main"
......