How to execute an application with nested module loading in Erlang? - erlang

I am creating an erlang application named (app_main) that will be initializing two other erlang applications ( app_1 and app_2 ):
Following is the structure of the code. Notice that app_1 and app_2 are under the deps directory and each have their own deps directory, which expand into further other nested references to other code directory structures, etc...
/app_main
/ebin
/main.erl
/deps
/app_1
/ebin
/deps
/...
/app_2
/ebin
/deps
/...
To start my app_main application, I am issuing the following command:
erl -pa ./ebin ./deps/*/ebin ./deps/*/deps/*/ebin -s app_main
The problem is that I need to know how many levels of nested deps references I may have... This sounds a bit illogical to me and I am wondering if there are better ways to achieve this.
For instance, would rebar be useful identifying nested rebar configs and add all dependencies to the app_main's deps directory automatically ?

Rebar is very useful for managing dependencies. You can define app 1 and 2 as dependencies for your main app and, assuming app 1 and 2 have their dependencies defined in Rebar, it will install them and their dependencies in your deps folder
See Rebar's documentation here: https://github.com/basho/rebar/wiki/Dependency-management

How are you specifying these dependencies, where do they come from, and how are you compiling them? When you're using 3rd party applications they will, in general, manage their own dependencies. Therefore, you should only ever to have start the shell like this:
erl -pa ebin deps/*/ebin
Both Rebar and erlang.mk are great tools for managing your applications and their dependencies.

Related

What is the correct way to load the code paths of dependencies in an Erlang application.

So assuming that i have an rebar app structure and in deps folder i have a number of dependencies, some are library applications and some are applications that need to be started. I usually do it like this:
start(_Type, _Args) ->
code:add_path("../deps/depapp/ebin"),
{ok, _} = application:ensure_all_started(depapp),
Is this the correct way to do it in an development environment ? How about in production ?
What you used is not necessarily a wrong way but could exposes some problems. For instance this way you have no choices for starting dependent applications which must be started before your application.
So there are other alternatives for loading or starting dependent OTP applications or libraries.
1) Using erl command line flags:
erl -pa ebin deps/*/ebing -s your_dep_app start -s your_app start
2) Using package manger for handling it:
As an example Rebar as a package manager can handle it for you. What you need is specifying your application dependencies in rebar.config and then issue rebar get-deps for Rebar2 or rebar3 compile for Rebar3. Following is a snippet of a sample config file for Rebar3:
{deps,[
%% Packages
rebar,
{rebar,"1.0.0"},
{rebar, {pkg, rebar_fork}}, % rebar app under a different pkg name
{rebar, "1.0.0", {pkg, rebar_fork}},
%% Source Dependencies
{rebar, {git, "git://github.com/erlang/rebar3.git"}},
{rebar, {git, "http://github.com/erlang/rebar3.git"}}]}.
For more information about Rebar dependency manager take a look at this link.
Also for starting or loading them using Rebar you can make a release and let Rebar start or load them. Following is a snippet of a sample Rebar config file for making a release:
{relx, [
{release,
{your_app, "0.1.0"},
[your_dep_app_1,
{your_dep_app_2, load}]}]}.
This config loads and starts your_dep_app_1 but just loads your_dep_app_2. For more information about Rebar release manager take a look at this link.

how to use modules that are deps in rebar.config

I am new to rebar and erlang in general and read that we can use other modules by specifying them as deps in rebar.config file. I am able to compile them properly but not sure how to use them in my module. If I try to use them, I get an error that the function is not available.
How can I use the functions from the modules that are in deps in my modules.
Thanks!
You have to tell code server where to find compiled code. I usually have in MakeFile something like:
run:
erl -pa ebin -pa deps/*/ebin
You usually need to specify application:start(appname) or similar in your application file before using them (to get some application to start up) and try to define them in the app.src file also, after that you should be able to use them

Erlang: Location of *.hrl files in multiple applications

I'm developing a series of related applications that will eventually be integrated into a single release. Several of the applications share identical record structures.
Should I:
a) Duplicate the *.hrl files that define the record structure in the include directory of each of the applications?
b) Put a single file somewhere else in my application tree--- and, if so where?
Here's my current tree:
zpt$
apps
app1
ebin
include
myrecords.hrl
priv
src
app2
ebin
include
myrecords.hrl
priv
src
etc
Many thanks,
LRP
One approach I tried was to create an application which doesn't do anything, but contains the record definitions common to multiple projects. Then I used rebar to include it as a dependency. When including the hrl files, I use the include_lib syntax. That syntax allows you to include hrl files from another application.
app
ebin
include
priv
src
some_src.erl
deps
common_hrl_app
include
common_records.hrl
src
ebin
other_dep_app
src
other_src.erl
.
.
.
include_lib example which could appear in either some_src.erl or other_src.erl:
-include_lib("common_hrl_app/include/common_records.hrl").
I like this approach because:
It plays nicely with the rebar dependency system
It allows me to track the hrls in one place in version control
I can version this application, which allows me to pull specific versions if I want a new application to be compatible with another using the same records.
Answering questions from the comments:
I have a skeleton app file in the ebin directory which specifies the name and version of the application so rebar can verify the version. Here's an example
{application,common_hrl_app,
[{description,[]},
{vsn,"1"},
{registered,[]},
{applications,[kernel,stdlib]},
{env,[]},
{modules,[]}]}.
With rebar, you have the top level application, which can have multiple applications as dependencies. When rebar fetches these dependencies, it places them in the deps directory. If any of those applications has their own dependencies, those are also fetched to the deps directory, and so on. There isn't an infinitely nested hierarchy of deps directories.

Can the `erl -make` command be extended to do things other than compiling .erl files?

I wanted to integrate Elixir into our project, and the good old codes don't use rebar, so I think writing the rules for building .ex files into Emakefile may be a good idea, yet the man page here didn't mention anything relevant.
Edit:
Our team works mainly in Windows environment, but the deployment will be done on Linux servers, so I need a cross-platform solution. Since Erlang itself is cross-platform, I wanted to do it with erl -make command.
Of course I can write a Makefile, but then I'll need a build.bat or something alike to build the code in our developing environments, since we don't have make command on our dev' machines.
Anyone have a better idea?
Update:
In case anyone wants to know, I'm doing it this way:
Copy the lib/elixir directory in the Elixir source tree to our source dir, say some_project/src/tools/elixir.
Add some_project/src/tools/elixir/src/elixir_transform.erl and some_project/src/tools/elixir/src/* to the Emakefile, in that order. Set the output dir to some_project/ebin (All the other .beam files are located there).
Copy src/elixir.app.src in the Elixir source tree to some_project/ebin/elixir.app, and edit it to fix the version code.
Build the Erlang code by running erl -pa ebin -make, in some_project dir.
Build the Elixir compiler by running erl -pa ebin -s elixir_compiler core -s erlang halt
Now we have a working Elixir environment in our code, and I use the following escript to build our custom .ex files:
%%! -pa ./ebin
main(_) ->
ExList = [
<<"source_1.ex">>,
<<"source_2.ex">>,
<<"source_3.ex">>],
application:start(elixir),
gen_server:call(elixir_code_server, {compiler_options, [{docs, true}, {debug_info, true}]}),
[elixir_compiler:file_to_path(F, <<"./ebin">>) || F <- ExList],
erlang:halt(0).
If you want to explicitly compile Elixir, I would go with the Makefile approach since it will always be supported by Elixir. However, I would recommend the precompiled binaries or even assume Elixir is installed in each developer machine. You can even add a task to your Emakefile to guarantee everyone is using the proper Elixir version.
Finally, about compiling your own Elixir code, I would recommend simply using mix. mix is a binary that ships with Elixir and you can simply do ./src/tools/elixir/bin/mix compile from your Emakefile.
In case using mix is not possible, you should use the parallel compiler, since it will compile files using all cores available in your machine and it will automatically detect and solve dependency in between files. Here is an example of calling the parallel compiler from erlang:
https://github.com/basho/rebar/pull/347/files#L1R62
The API is very simple. It expects a list of file names to compile as binary and the directory to output files to as another binary.

How to install application in Erlang?

In my project, i want to use mysql so i checkout this https://github.com/dizzyd/erlang-mysql-driver. I want to know how install the application so that my project can interact with it
Have a look at "rebar" - https://bitbucket.org/basho/rebar/wiki/Home
It can be used for installing dependencies, and for creating independent releases.
And a quick look at erlang-mysql-driver, that you want to use, shows that it is also using rebar for its dependency management.
rebar may complicate things if you have already started laying out your app (done some coding already) or if you are a newbie , however, if your project is an erlang/OTP app, then i suggest that you first organize you code according to the recommended file system like this:
MyProject--/src
/ebin
/lib
/include
/priv
/doc
/examples
/test
/Emakefile
The Emakefile is an important file. It maynot have a file extension. It enables the BIF: make:all() to compile all the erlang source modules you point it to and transfers all the .beam files to the destination you want.
For example: is i want all the modules in src to be compiled and transfer the beam files into ebin, i enter this into the Emakefile
{"src/*", [debug_info, netload,strict_record_tests,warn_obsolete_guard,{outdir, "ebin"}]}.
In that case i would start the erlang shell with its pwd() pointing in the folder MyProject, to enable the function call make:all() to find the file Emakfile so as to compile all my src files.
Now, suppose you have another OTP app which you want to have as an extra package in your build. If it OTP-like arranged as i have showed you, and not yet built i.e. not yet made, i mean with only its src and its folder ebin are empty or it ebin may be containing a .APP file already. Then you copy this OTP application into your lib folder, so that your application looks like this:
MyProject--/src
/ebin
/lib/some_otp_app-1.0
/include
/priv
/doc
/examples
/test
/Emakefile
then we would change our Emakefile to look like this:
{"src/*", [debug_info, netload,strict_record_tests,warn_obsolete_guard,{outdir, "ebin"}]}.
{"lib/some_otp_app-1.0/src/*", [debug_info, netload,strict_record_tests,warn_obsolete_guard,{outdir, "lib/some_otp_app-1.0/ebin"}]}.
In the folder MyProject, you can put a shell script that will start your project and add all relevant ebin paths to your nodes code path.the sh script may look like this:
#!/bin/bash
erl \
-name my_node#my_domain \
-pa ./ebin ./lib/*/ebin ./include \
-mnesia dump_log_write_threshold 10000 \
-eval "make:all()"
You can save this file as start_project.sh. Hence as you make changes to your source code, even at the time of starting your project, when you run the sh script with your terminal path pointing into the folder: MyProject, you do this:
$pwd
/export/home/your_user_name/MyProject
$sh start_project.sh
This would start your project at the node you entered in the script and would compile all src files which were changed when it was off. Not only this, you can as well call: make:all() in your shell whenever you make cahnges to your src code. then you would call: l(some_module) after making so that the erlang vm reloads the new object code of the compiled module.
So, your entire project will now appear like this:
MyProject--/src
/ebin
/lib/some_otp_app-1.0
/include
/priv
/doc
/examples
/test
/Emakefile
/start_project.sh
So if you substitute the erlang driver for mysql application with this "some_otp_app-1.0", everything will be fine. success!

Resources