ic(3)

πŸ“¦
version 5.2.2 Β· 2 versions
, in fedora 43, fedora 44

πŸ’– ic(3) β€” Erlang Module Definition πŸ’–

okay bestie, sit down, hydrate, because we're about to get into ic, the Erlang IDL Compiler, and it is lowkey iconic. this is the manual page, and yes i'm gonna make it cute, but no cap β€” every single flag, every type, every default is staying right here. 🫢


✨ NAME ✨

ic β€” the Erlang IDL Compiler. that's it. that's the name. periodt. πŸ’…


🌸 DESCRIPTION 🌸

so the ic module is an Erlang implementation of an OMG IDL compiler. depending on which back-end you pick, the code it spits out maps to Erlang, C, or Java. and bestie, it's not just sitting there β€” the compiler generates client stubs and server skeletons for you. main character behavior, honestly. πŸ‘‘

for each scope, two kinds of files get generated:

  • Ordinary code files β€” these contain the object interface functions. the actual doing-stuff files.
  • Header files β€” these are the ones used for defining record definitions.

so yeah. header files = record definitions, ordinary files = interface functions. got it? got it. ✨


πŸŽ€ EXPORTS πŸŽ€

here's where the real ones live. the functions. the calls. let's go. πŸ’–

🌈 ic:gen/1 and ic:gen/2

ic:gen(FileName) -> Result
ic:gen(FileName, [Option]) -> Result

πŸ’œ Types

okay so this is the type situation, and i'm putting it verbatim because every single bit of it matters, bestie:

Result = ok | error | {ok, [Warning]} | {error, [Warning], [Error]}

Option = [ GeneralOption | CodeOption | WarningOption | BackendOption]

GeneralOption =
    {outdir, String()} | {cfgfile, String()} | {use_preproc, bool()} |
    {preproc_cmd, String()} | {preproc_flags, String()}

CodeOption =
    {gen_hrl, bool()} | {serv_last_call, exception | exit} |
    {{impl, String()}, String()} | {light_ifr, bool()}
    this | {this, String()} | {{this, String()}, bool()} |
    from | {from, String()} | {{from, String()}, bool()} |
    handle_info | {handle_info, String()} | {{handle_info, String()}, bool()} |
    timeout | {timeout, String()} | {{timeout, String()}, bool()} |
    {scoped_op_calls, bool()} | {scl, bool()} |
    {user_protocol, Prefix} |
    {c_timeout, {SendTimeout, RecvTimeout}} |
    {c_report, bool()} |
    {precond, {atom(), atom()}} | {{precond, String()} {atom(), atom()}} |
    {postcond, {atom(), atom()}} | {{postcond, String()} {atom(), atom()}}

WarningOption =
    {'Wall', bool()} | {maxerrs, int() | infinity} |
    {maxwarns, int() | infinity} | {nowarn, bool()} |
    {warn_name_shadow, bool()} | {pedantic, bool()} |
    {silent, bool()}

BackendOption = {be, Backend}

Backend = erl_corba | erl_template | erl_plain | erl_genserv |
    c_client | c_server | java

DirNAme = string() | atom()
FileName = string() | atom()
🫢 little shortcut for you: the tuple {Option, true} can be replaced by just Option for boolean values. so you don't have to write the true out. slay.

πŸ¦‹ Calling it from the command line

the ic:gen/2 function can also be called from the command line, like this:

erlc "+Option" ... File.idl

and here's an example, because examples are self-care:

erlc "+{be,c_client}" '+{outdir, "../out"}' File.idl

⭐ GENERAL OPTIONS ⭐

these are the general ones. the foundational ones. the ones that set the whole vibe. let's break them down, one by one, no skipping. πŸ‘‘

  • outdir β€” places all the output files in the directory given by the option. and if that directory doesn't exist yet? it gets created. we love a proactive king. πŸŽ€

Example option: {outdir, "output/generated"}.

  • cfgfile β€” uses FileName as the configuration file. options will override the compiler defaults, but they can themselves be overridden by command line options. so it's a whole hierarchy, bestie. the default value is ".ic_config". πŸ’…

Example option: {cfgfile, "special.cfg"}.

  • use_preproc β€” uses a preprocessor. the default value is true.
  • preproc_cmd β€” this is the command string to invoke the preprocessor. the actual command gets built as preproc_cmd++preproc_flags++FileName. yes, that's literally how it's constructed, i don't make the rules. 🌸

Example option: {preproc_cmd, "erl"}).

Example option: {preproc_cmd, "gcc -x c++ -E"}.

  • preproc_flags β€” the flags you give to the preprocessor. πŸ¦‹

Example option: {preproc_flags, "-I../include"}.


and that's the general options wrapped, but bestie we are so not done β€” the code options, warning options, and back-end options are still coming. stay hydrated. πŸ’–

✨ CODE OPTIONS ✨

Ok bestie, these are the options that literally change what code comes out the other end. Read carefully πŸ’…

  • light_ifr β€” rn the default is false. To even use this one, Orber has to be configured to use Light IFR (go peek at Orber's User's Guide, iykyk). When you turn it on, the generated files that register the API in the IFR DB get minimized β€” smol files, big slay.

Example option: {light_ifr, true}.

  • gen_hrl β€” generate header files. Default is true. πŸŽ€
  • serv_last_call β€” makes the last gen_server handle_call either raise a CORBA exception or just exit plainly. Default is the exception.
  • {{impl, IntfName}, ModName} β€” this one assumes the interface named IntfName is implemented by the module named ModName, and it'll generate calls to the ModName module in the server behavior. Heads up bestie: IntfName must be a fully scoped name, like "M1::I1". No shortcuts. πŸ’€
  • this β€” adds the object reference as the first parameter to the object implementation functions. Basically it makes the implementation aware of its own object reference, main character energy fr.

The option comes in three varieties: this which activates the parameter for all interfaces in the source file, {this, IntfName} which activates the parameter for a specified interface, and {{this, IntfName}, false} which deactivates the parameter for a specified interface.

Example option: this) activates the parameter for all interfaces.

Example option: {this, "M1::I1"} activates the parameter for all functions of M1::I1.

Example options: [this, {{this, "M1::I2"}, false}] activates the parameter for all interfaces except M1::I2.

  • from β€” adds the invoker's reference as the first parameter to the object implementation two-way functions. And if you use both from and this, the invoker's reference parameter gets passed as the second parameter instead. This is what makes it possible for the implementation to respond to a request and keep executing afterwards β€” no cap. Consult the gen_server and Orber docs for how you might use this one. 🫢

The option comes in three varieties: from which activates the parameter for all interfaces in the source file, {from, IntfName} which activates the parameter for a specified interface, and {{from, IntfName}, false} which deactivates the parameter for a specified interface.

Example option: from) activates the parameter for all interfaces.

Example options: [{from, "M1::I1"}] activates the parameter for all functions of M1::I1.

Example options: [from, {{from, "M1::I2"}, false}] activates the parameter for all interfaces except M1::I2.

  • handle_info β€” makes the object server call a function handle_info in the object implementation module on all unexpected messages. Super useful if your object implementation needs to trap exits. πŸ¦‹

Example option: handle_info will activates module implementation handle_info for all interfaces in the source file.

Example option: {{handle_info, "M1::I1"}, true} will activates module implementation handle_info for the specified interface.

Example options: [handle_info, {{handle_info, "M1::I1"}, false}] will generate the handle_info call for all interfaces except M1::I1.

  • timeout β€” lets the user set a server response time limit. This should be a string that represents the scope for the interface which should have an extra variable for wait time initialization.

Example option: {timeout,"M::I"}) produces server stub which will has an extra timeout parameter in the initialization function for that interface.

Example option: timeout produces server stub which will has an extra timeout parameter in the initialization function for all interfaces in the source file.

Example options: [timeout, {{timeout,"M::I"}, false}] produces server stub which will has an extra timeout parameter in the initialization function for all interfaces except M1::I1.

  • scoped_op_calls β€” used to produce more refined request calls to the server. When you set this to true, the operation name mentioned in the call gets scoped. This is essential to avoid name clashes when talking to c-servers. It's available for the c-client, c-server and the Erlang gen_server back ends. ⭐ Important: all the parts generated by ic have to agree on the use of this option. Default is false.

Example options: [{be,c_genserv},{scoped_op_calls,true}]) produces client stubs which sends "scoped" requests to a gen_server or a c-server.

  • user_protocol β€” used to define your own protocol, different from the default Erlang distribution + gen_server protocol. Currently only valid for C back-ends. For further details see IC C protocol.

Example options: [{be,c_client},{user_protocol, "my_special"}]) produces client stubs which use C protocol functions with the prefix "my_special".

  • c_timeout β€” makes sends and receives have timeouts (C back-ends only). These timeouts are specified in milliseconds.

Example options: [{be,c_client},{c_timeout, {10000, 20000}}]) produces client stubs which use a 10 seconds send timeout, and a 20 seconds receive timeout.

  • c_report β€” generates code for writing encode/decode errors to stderr (C back-ends only). timeouts are specified in milliseconds.

Example options: [{be,c_client}, c_report]).

  • scl β€” for compatibility with previous compiler versions up to 3.3. So here's the tea: because of better semantic checks on enumerants, the compiler finds name clashes between user defined types and enumerant values in the same name space. Enable this option and the compiler turns off the extended semantic check on enumerant values. Default is false.

Example option: {scl,true}

  • precond β€” adds a precondition call before the call to the operation implementation on the server side. πŸ‘‘

The option comes in three varieties: {precond, {M, F}} which activates the call for operations in all interfaces in the source file, {{precond, IntfName}, {M, F}} which activates the call for all operations in a specific interface, and {{precond, OpName}, {M, F}} which activates the call for a specific operation.

The precondition function has the following signature m:f(Module, Function, Args).

Example option: {precond, {mod, fun}} adds the call of m:f for all operations in the idl file.

Example options: [{{precond, "M1::I"}, {mod, fun}}] adds the call of m:f for all operations in the interface M1::I1.

Example options: [{{precond, "M1::I::Op"}, {mod, fun}}] adds the call of m:f for the operation M1::I::Op.

  • postcond β€” adds a postcondition call after the call to the operation implementation on the server side. ✨

The option comes in three varieties: {postcond, {M, F}} which activates the call for operations in all interfaces in the source file, {{postcond, IntfName}, {M, F}} which activates the call for all operations in a specific interface, and {{postcond, OpName}, {M, F}} which activates the call for a specific operation.

The postcondition function has the following signature m:f(Module, Function, Args, Result).

Example option: {postcond, {mod, fun}} adds the call of m:f for all operations in the idl file.

Example options: [{{postcond, "M1::I"}, {mod, fun}}] adds the call of m:f for all operations in the interface M1::I1.

Example options: [{{postcond, "M1::I::Op"}, {mod, fun}}] adds the call of m:f for the operation M1::I::Op.


🚨 WARNING OPTIONS 🚨

Not me putting these in a separate lil section bc they're all about the yelling. πŸ™ˆ

  • 'Wall' β€” activates all reasonable warning messages, in analogy with the gcc -Wall option. Default value is true.
  • maxerrs β€” the maximum numbers of errors that can be detected before the compiler gives up. The option can either have an integer value or the atom infinity. Default number is 10.
  • maxwarns β€” the maximum numbers of warnings that can be detected before the compiler gives up. The option can either have an integer value or the atom infinity. Default value is infinity.
  • nowarn β€” suppresses all warnings. Default value is false.
  • warn_name_shadow β€” a warning appears whenever names are shadowed due to inheritance; for example, if a type name is redefined from a base interface. ⚠️ Note that it is illegal to overload operation and attribute names, as this causes an error to be produced. Default value is true.
  • pedantic β€” activates all warning options. Default value is false.
  • silent β€” suppresses compiler printed output. Default value is false.

🌈 BACK-END OPTIONS 🌈

Which back-end IC will generate code for is determined by the supplied {be,atom()} option. If you leave it out, erl_corba is used. Currently, IC supports the following back-ends:

  • erl_corba β€” this option switches to the IDL generation for CORBA.
  • erl_template β€” generate CORBA call-back module templates for each interface in the target IDL file. πŸŽ€ Note, will overwrite existing files.
  • erl_plain β€” will produce plain Erlang modules which contain functions that map to the corresponding interface functions on the input file.
  • erl_genserv β€” this is an IDL to Erlang generic server generation option.
  • c_client β€” will produce a C client to the generic Erlang server.
  • c_server β€” will produce a C server switch with functionality of a generic Erlang server.
  • java β€” will produce Java client stubs with functionality of a generic Erlang server. β˜•
  • c_genserv β€” Deprecated. Use c_client instead. bestie pls upgrade 😭

🧚 PREPROCESSOR 🧚

The IDL compiler allows several preprocessors to be used β€” the Erlang IDL preprocessor or other standard C preprocessors. Options can be used to provide extra flags such as include directories to the preprocessor. The build in the Erlang IDL preprocessor is used by default, but any standard C preprocessor such as gcc is adequate. πŸ’œ

The preprocessor command is formed by appending the prepoc_cmd to the preproc_flags option and then appending the input IDL file name.


πŸ’– CONFIGURATION πŸ’–

The compiler can be configured in two ways:

  • Configuration file
  • Command line options

The configuration file is optional and overrides the compiler defaults and is in turn overridden by the command line options. The configuration file shall contain options in the form of Erlang terms. The configuration file is read using file:consult.

An example of a configuration file, note the "." after each line. ⭐

{outdir, gen_dir}.
{{impl, "M1::M2::object"}, "obj"}.

✨ OUTPUT FILES ✨

okay bestie, this is the part where your hard work actually turns into stuff on disk πŸ’Ύβœ¨ The compiler will produce output in several files depending on scope declarations found in the IDL file.

At most three file types will be generated for each scope (and yes, that includes the top scope too πŸ‘‘) β€” it all depends on the compiler back-end and the compiled interface.

Generally, the output per interface will be:

  • a header file (.hrl/ .h) πŸ“„
  • and one or more Erlang/C files (.erl/.c) πŸ—‚οΈ
lowkey important: please look at the language mapping for each back-end for details, bestie β€” that's where the full tea is πŸ«–

Some things you can count on, fr fr:

  • There will be at least one set of files for an IDL file, for the file level scope πŸŽ€
  • Modules and interfaces also have their own set of generated files πŸ’…

so yeah, one IDL file in, a whole little friend group of files out πŸŒˆπŸ¦‹ slay.


and that's the whole page, bestie!! πŸ’– this beauty is from Ericsson AB, it's version ic 5.2.2, and it's living in section ic(3) β€” go forth and compile, you absolute icon πŸ‘‘βœ¨

πŸ“¦ where this page comes from