wiki:ManagingFiles

Version 4 (modified by tothm, 9 years ago) (diff)

--

Managing your files and applications

Adding files and directories

You can add files to the RefactorErl database by calling the add function with either a filename as a string or a module name as an atom. Note that in the latter case, "ri" defaults to the current working directory (which you may work around by including a path in your singe-quoted atom). If you specify a directory instead of a regular filename, then it will be recursively traversed. You may just as well give a list of atoms or strings to add more files at once. All of the following example commands would add the same file:

 cd(dir), ri:add(modname).
 ri:add('dir/modname').
 ri:add(['dir/modname']).
 ri:add("dir/modname.erl").
 ri:add("/current/dir/modname.erl").
 ri:add("path_to_dir/dir").

Usually, Erlang source files (having the extension .erl) are loaded into RefactorErl. In addition, RefactorErl is also capable of loading compiled .beam files.

ri:add("compiled.beam").

Note that this feature is applicable only to those .beam files that were compiled with the debug_info option. Also note that the resulting file will be pretty printed by RefactorErl.

Dropping files and directories

The module displays the progression of loading. Removing files from the database is similarly easy and also recursive. The following will equally work:

 ri:drop(modname).
 ri:drop([modname]).
 ri:drop("dir/modname.erl").
 ri:drop("/current/dir/modname.erl").
 ri:drop("path_to_dir/dir").

Adding applications

Modules can be loaded as applications, but some configurations has to be made before. The configurations can be done manually by setting the enviromental nodes, or can be done automatically by using the Emakefile handling functions (beta version) of the tool.

Adding applications by manual configuration

The manually configuration can be done by using the ri or the ris module.

Modules can be loaded as applications, but the base of your library has to be set before:

ri:addenv(appbase, "path/to/my/applib").

If the application has additional include directories, then these directories has to be also set in advance:

ri:addenv(include, "path/to/my/incldir").

If the application build process contains compile-time macros, then these macros also can be set:

%if it is only used in ifdef forms.
ri:addenv(def, 'my_macro').

ri:addenv(def, {'my_macro', my_macro_value}).

If the include forms of the application contain OS based enviromental nodes, then these nodes can be set:

ri:addenv(env_var, {os_env_name, "os_env_path"}).

Let's see an example:

The Emakefile has the following contents:

{"/home/user/dev/lib/app1/src/*",
   [{i,"/home/user/dev/lib/"},
    {d,'MY_MACRO', "ITS_VALUE"},
    {outdir,"/home/user/dev/lib/app1/ebin"}]}.

{"/home/user/dev/lib/app2/src/*",
   [{i,"/home/user/dev/lib/"},
    {d,'MY_MACRO', "ITS_VALUE"},
    {outdir,"/home/user/dev/lib/app2/ebin"}]}.

{"/home/user/dev/lib/app3/src/*",
   [{i,"/home/user/dev/lib/share/include/"},
    {i,"/home/user/dev/lib/"},
    {outdir,"/home/user/dev/lib/app3/ebin"}]}.

To add app1, app2, app3 to RefactorErl you should do the followings:

% add app1, app2 with the same configuration
ri:addenv(appbase, "/home/user/dev/lib/"),
ri:addenv(def, {'MY_MACRO', "ITS_VALUE"}),

Apps = [app1, app2],
[ri:add(home, App) || App <- Apps].

% add app3 after the configuration has been modified
ri:delenv(def),
ri:addenv(include, "/home/user/dev/lib/share/include/"),

ri:add(home, app3).

You can check the already given application base directories by listing all of the enviromental nodes:

ri:envs().

It is possible to delete the defined environment variables:

ri:delenv(include).

Or you can set an environmental variable to another value:

ri:setenv(env_name, "path/to/new_value").

The following enviromental nodes can be set via the ri module:

output: Where does RefactorErl write changes in?

appbase: The list of the used application based directories.

include: The list of the used include directories.

def: The list of the compile-time macros.

env_var: The list of the OS based enviromental nodes.

Adding applications using the Emakefile handling functions

Applications with their proper configurations (such as include paths, macros), or only the configurations, which are defined in an Emakefile, can be added by executing only one command. The given Emakefile is parsed then the configurations and the adding procedure are handled automatically by the tool. The functionality is available in the ri module and also available in the ris module.

By executing one of the following commands:

EmakeFilePath = "/absolute_path_to_the_emakefile",
ri:add_by_emakefile(EmakefilePath).

% whether a list of Emakefiles are present.
EmakeFiles = ["/absolute_path_to_the_emakefile1", "/absolute_path_to_the_emakefile2"],
ri:add_by_emakefile(EmakeFiles).

the applications which are defined in the given Emakefile(s) are loaded into the database. Please note, that

  • the configurations, which are defined in the Emakefile, only stored temporary, while the adding procedure has not been finished;
  • if the Emakefile contains relative path, then it will be converted absolute by using the path of the Emakefile as base.

If your apllications have been loaded by using ri:add_by_emakefile/1 and any of the applications uses irregular include path or compile-time macro, then these applications should be updated by using ri:add_by_emakefile/1, or by loading the configuration ri:load_configuration/1 then by executing ri:database_synchronization/0.
If the loaded configuration is not needed further then it may be unloaded by executing ri:unload_configuration/1.

Applications can be dropped from the database in the same way as the normal files.

!comment
===  Adding applications using unified build tools ri:add_app(1|2) ===

The main goal of this approach to make the application loading method simpler to the end user.

The loading of files are simplified to the ri:add_app(1|2) interface that is extensible and can support various build tools.

Control flow details 
{{{#!erlang
ri:add_app(1|2) ==> reflib_ui_router:request/2 ==> reflib_ui:add_app/3 ==> 
==> refcore_apploader:add_app/3 ==> refcore_confparser:parser/2 ==> refcore_$(parser):parse/2
               +
    refcore_apploader:add_files                                             $(parser) : [rebar, emakefile, erlang_mk, makefile]
}}}

The confparser tries to recognize the parser primarily by the name of the file if the passed path is a regular file (rebar, Emakefile, erlang.mk, Makefile).
When the passed path is a directory the following priority takes place in detecting the appropriate parser : rebar, rebar.config, Emakefile, erlang.mk, Makefile.
See the configuration of various parsers below:

see [#point1 Rebar Parser]

see [#point2 Emakefile Parser]

see [#point3 erlang_mk Parser]

see [#point4 Makefile Parse]

[=#point1] 
==== Rebar Parser ====

The application directory layout must follow the Rebar and OTP conventions (src, priv, include, ebin), for further details please refer to https://github.com/rebar/rebar/wiki.\\
The rebar parser reads recursively the directories and the rebar.config files.\\
The subdirectories inhert the the rebar.config options from the parent directories the rebar.config options from the actual directory may overrides them.


Interface options refcore_rebar_parser:parse/2
* BaseDirectoryOrRebarPath - Application base dir or the path including the rebar.config file
* UserOptions - user properties [{rebar_load_tests, true}]

The parser extracts the following options from rebar.config file:
   * lib_dirs - additional library directories to add to the code path
   * erl_first_files - rrlang files to compile before the rest
   * erl_opts - erlang compiler options
   * sub_dirs - subdirectories
   * deps_dir - location of dependencies.
   * deps - dependencies 
When the user property rebar_load_tests is set to true the tests related options are also loaded:
   * ct_dir - test SUITEs location
   * ct_extra_params - option to pass extra parameters when launching Common Test (-dir, -include, -spec ) 
   * eunit_first_files - same as erl_first_files, but used only when running 'eunit'
   * eunit_compile_opts - additional compile options for eunit. erl_opts is also used (-dir, -include, -spec ), 
   * qc_first_files - same as erl_first_files, but used only when running 'qc'
   * qc_compile_opts - additional compile options for qc. erl_opts is also used (-dir, -include, -spec ) 

Further details and a more complete example of the rebar.config file is available at: https://github.com/rebar/rebar/blob/master/rebar.config.sample
[=#point2 ] 
==== Emakefile Parser ====
The Emakefile Parser consults the given Emakefile and extracts the files and other information (appbase,def).

Example Emakefile:
{{{#!erlang
{'src/hello',

   [debug_info,
     {i,"include"},
     {outdir, "ebin"}
   ]

}.

{'src/*',
   [debug_info]
}.
}}}

Further reading: http://www.erlang.org/doc/man/make.html.

Interface options refcore_emakefile_parser:parse/2
* DirectoryOrEmakefilePath - Path to the Emakefile or to the application directory
* _  - second argument is ignored during the Emakefile parsing.
[=#point3 ] 
==== erlang_mk Parser ====
Expected directory structure: src, ebin, test
Application requirements:
erlang.mk contains common makefile rules for building an application.\\
Use it together with GNU make, add it to your project an include it in your Makefile:
{{{
#!sh
PROJECT = my_project
include erlang.mk
}}}
It provides a set of core rules like all, deps, app, rel, docs, tests, clean, etc...

A convenient way to create an application skeleton using erlang.mk is:
{{{
#!sh
make -f erlang.mk bootstrap
}}}

Further reading: https://github.com/ninenines/erlang.mk

Interface options refcore_erlang_mk_parser:parse/2
* DirectoryOrErlMkPath - Path to the Makefile or to the application directory
* _  - second argument is ignored during the Emakefile parsing.

The parser reads the files from the src, test directories. \\Also processes the Makefile and checks the presence of the required "include erlang.mk" keywords and reads the variables below:
* ERLC_OPTS - compilation options 
* DEPS_DIR - location of the dependencies
* DEPS - names of the dependencies

[=#point4 ] 
==== Makefile Parser ====
The Makefile Parser tries to evaluate three kind of macros directly from the Makefile.\\

Default Makefile variables:
* INCDIR - Makefile macro that contains the includes
* CXC_TARGET - Makefile macro that contains the destination files
* CXA_TARGET - Secondary include Makefile macro.

The default Makefile macros can be overridden via ri:add_app("....", UserOptions) \\
UserOptions = [{include_macro, 'INCDIR'}, {target_macro, 'CXC_TARGET'}, {sec_include_macro,'CXA_TARGET'}] \\


Further reading: http://www.gnu.org/software/make/manual/make.html

Refreshing your database

If your files has been changed in the disk, since the last load, then these files may be re-added to the database to gather fresh information about the files from the tool. This can be easily done, by executing one of the following commands:

ri:database_synchronization().

ris:database_synchronization().

Gathering information about your database

For convenience, both the filenames and the directory names can be given as atoms as well as strings. The list of loaded files can be obtained by calling

ri:ls().

This call also displays the status of the loaded files (error or no_error). If the module m is loaded,

ri:ls(m).

will give information about the functions, records and macros in the file. The contents of a file can be listed by

ri:cat(ModFile).

The content of a function can be listed by

ri:cat(ModFile, {FunName,Arity}).