IMPORT FGL
The IMPORT FGL
instruction imports module symbols.
Syntax
IMPORT FGL {
module-name
|
package-path.*
|
package-path.module-name
}
- module-name is an identifier defining the module to be imported (without the file extension).
- package-path is a dot-separated list of identifiers specifying a package path, to
import a set of modules defined with the
PACKAGE
instruction. - The identifiers specified with
IMPORT FGL
are case sensitive. The directory names and module file names in the file system must match the character case.
Usage
With IMPORT FGL module-name
or IMPORT FGL
package-path.module-name
, the symbols of the named
module can be referenced in the current module.
Using IMPORT FGL package-path.*
, is equivalent to importing
all modules that belong to this package.
When specifying a package path with IMPORT FGL package-path.*
or IMPORT FGL package-path.module-name
, the
modules must have been marked with the PACKAGE
instruction.
Packages can be organized hierarchically in a tree of directories. However, when using
IMPORT FGL package-path.*
, only modules that belong to this
package are imported. If package directories exist below package-path, they will
not be imported recursively.
IMPORT FGL package-path.*
when the
package contains a lot of modules: The compiler will have to process all imported modules.At runtime, the imported modules are only loaded on demand, when the program flow reaches an instruction that uses an element of the imported module. For example, when calling a function or when assigning a (public) module variable of the imported module.
The names specified after the IMPORT FGL
instruction are case-sensitive.
The imported module symbols that can be referenced are:
Compilation with IMPORT FGL
IMPORT FGL
instructs the fglcomp
compiler and fglrun runtime system to load/check the
specified modules.
When using only IMPORT FGL
to define module dependency, there is no longer a
need to link programs or use libraries.
With IMPORT FGL
, the compiler can check the number of parameters and returning
values in functions calls, and the autocompletion in source code editors is improved as it can
suggest all imported symbols.
By default, the fglcomp compiler creates the .42m files
in the current working directory, or beside the .4gl source files of modules
imported via packages. Consider using the --output-dir
option of
fglcomp, if you want to create the .42m files in a different
(runtime) directory as the current development directory. For more details, see Output directory for .42m pcode files.
Auto-compilation of imported modules
When the imported module is located in the same directory as the compiled module or in a sub-directory following the package paths, fglcomp will automatically compile the imported module, if the .42m file of the imported module does not exist, or is older than the corresponding source file.
The automatic recompilation of imported modules applies recursively for local modules: For
example, when a main.4gl module imports module1
which in turn
imports module2
, and module2.4gl is more recent as
module2.42m, fglcomp will automatically compile
module2.4gl. Recursive automatic compilations works only for modules imported
directly and located in the current working directory.
The FGLLDPATH environment variable
specifies the directories to search for the .42m modules used by IMPORT
FGL
. When not using packages, auto-compilation of imported modules is only supported in the
current directory. Modules located in other directories and found by FGLLDPATH and must already be
compiled. When using packages, fglcomp can find modules in sub-directories by
following the package-path specified in IMPORT FGL package-path.*
. Therefore,
FGLLDPATH should not be set during compilation with packages. FGLLDPATH can be set at runtime to
find .42m modules from a top-dir that is different from the
source top-dir.
--implicit=none
option of fglcomp. If the .42m file exists but the
.4gl source file cannot be found, fglcomp imports the
.42m file as is.Circular module references
Circular references between modules are allowed. A circular reference occurs when several modules
define an interdependence with IMPORT FGL
.
- Direct circular reference: Module A imports module B, which in turn imports module A.
- Indirect circular reference: Module A imports module B, which imports module C, which imports module A.
When .42m files are not yet available, compiling modules with circular
dependency is only possible when these modules exist in the same directory or in a package
sub-directory, and auto-compilation is used (option --implicit=none
is not used). Otherwise,
all interdependent modules must be specified in the fglcomp command.
IMPORT FGL module_b
MAIN
CALL function_a1()
END MAIN
FUNCTION function_a1()
DISPLAY "In module_a: function_a1()"
CALL module_b.function_b1()
END FUNCTION
FUNCTION function_a2()
DISPLAY "In module_a: function_a2()"
END FUNCTION
IMPORT FGL module_a
FUNCTION function_b1()
DISPLAY "In module_b: function_b1()"
CALL module_a.function_a2()
END FUNCTION
--verbose
option of
fglcomp.$ fglcomp --verbose module_a.4gl module_b.4gl
[parsing module_a.4gl]
[parsing module_b.4gl]
[building module_b]
[writing module_b.42m]
[building module_a]
[writing module_a.42m]
$ fglrun module_a
In module_a: function_a1()
In module_b: function_b1()
In module_a: function_a2()
Identifying modules to be imported
When migrating existing projects using traditional linking, after compiling all the
.4gl sources, consider using the --print-missing-imports
option of fgllink or fglrun, to print the IMPORT
FGL
suggestions for all the modules specified in the fglrun command
line. On the other hand, the --print-imports
option reports all imported modules
that are really used.
--print-missing-imports
to the fgllink or
fglrun -l commands, to identify what modules should be
imported:$ head *.4gl
==> main.4gl <==
MAIN
CALL func1()
END MAIN
==> mod1.4gl <==
FUNCTION func1()
CALL func2()
END FUNCTION
==> mod2.4gl <==
FUNCTION func2()
END FUNCTION
$ fglcomp *.4gl
$ fgllink --print-missing-imports -o prog.42r *.42m
-- in main.4gl
IMPORT FGL mod1
-- in mod1.4gl
IMPORT FGL mod2
The --print-missing-imports
option will try to resolve all (function) symbols as
done during linking, but instead of producing a .42r program, it will list the
IMPORT FGL
instructions to be added in each module, and thus avoid linking.
The --print-imports
option prints all IMPORT FGL
instructions,
that are really used by a module, where at least one symbol (function, variable, constant, type,
etc) is used by the importing module.
Consider using the --print-missing-imports
options instead of
--print-imports
: If you are missing an IMPORT FGL
for a
non-function symbol like a variable, constant, or type, the compiler will produce a compilation
error. Regarding function symbols, the compilation is possible even when the module was not
imported. Therefore, --print-missing-imports
is useful to identify modules to be
imported to resolve function symbols and avoid linking.
mod1
can be imported in main
, but
mod2
already imports
mod1
:$ head *.4gl
==> main.4gl <==
MAIN
CALL func1()
END MAIN
==> mod1.4gl <==
FUNCTION func1()
CALL func2()
END FUNCTION
==> mod2.4gl <==
IMPORT FGL mod1
FUNCTION func2()
CALL func1()
END FUNCTION
$ fglcomp main.4gl mod1.4gl mod2.4gl
$ fglrun --print-imports main.42m mod1.42m mod2.42m
-- in main.4gl
IMPORT FGL mod1
-- in mod1.4gl
IMPORT FGL mod2
-- in mod2.4gl
IMPORT FGL mod1
$ fglrun --print-missing-imports main.42m mod1.42m mod2.42m
-- in main.4gl
IMPORT FGL mod1
-- in mod1.4gl
IMPORT FGL mod2
Scope of module symbols (PRIVATE/PUBLIC)
The PRIVATE
/PUBLIC
modifiers can be used to hide / publish
symbols to other modules.
For backward compatibility, functions are by default public. Module variables, types and constants are by default private.
PUBLIC DEFINE custlist DYNAMIC ARRAY OF RECORD
id INT,
name VARCHAR(50),
address VARCHAR(200)
END RECORD
...
PRIVATE FUNCTION myfunction()
...
END FUNCTION
Resolving symbol name conflicts with module prefix
If a symbol is defined twice with the same name in two different modules, the symbol must be qualified by the name of the module or the package name, when the module belongs to a package.
This feature overcomes the traditional 4GL limitation, requiring unique function names within a program.
In the following example, both imported modules define the same "init()
"
function, but this can be resolved, by adding the module name followed by a dot before the function
names:
IMPORT FGL orders
IMPORT FGL customers
MAIN
CALL orders.init()
CALL customers.init()
...
END MAIN
If a symbol is defined twice with the same name in the current and the imported module, an unqualified symbol will reference the current module symbol.
The following example calls the "init()
" function with and without a module
qualifier. The second call will reference the local function:
IMPORT FGL orders
MAIN
CALL orders.init() -- orders module function
CALL init() -- local function
...
END MAIN
FUNCTION init()
...
END FUNCTION
IMPORT FGL myshop.* -- imports several modules including "orders"
MAIN
CALL myshop.orders.init()
...
END MAIN
Qualifying imported symbols with fglcomp --qualify-imports
To make the code more readable, check for unqualified symbols with the -W
unqualified-imports
warning, and consider using the --qualify-imports
option of fglcomp to refactor the sources.
The --qualify-imports
option is part of the code refactoring tools provided by
Genero BDL.
For more details, see Qualifying imported symbols.
Mixing IMPORT FGL and .42r linking
Traditional linking is still supported for backward compatibility. To ease migration from
traditional linking to imported modules, you can mix IMPORT FGL
usage with
fgllink/fglrun -l.
By default, even when IMPORT FGL
is used, fglcomp does not
raise an error, if a referenced function is not found in the imported modules. This is mandatory to
compile the 42m file to be linked later with the module defining the missing
function.
Use the fglcomp -W implicit
or the
--resolve-calls
options, to check that all symbols are resolved with a
corresponding IMPORT FGL
instruction.
When the -W implicit
option is used , fglcomp will print
warning -8406 for any
referenced function that cannot be found in an imported module.
The -W implicit
option is to be used when migrating linked modules to a solution
where module dependency is only based on IMPORT FGL
.
To enable strict symbol resolution by the compiler, use the --resolve-calls
option. This option will force the compiler to check all function symbols referenced in a module,
and raise error -8406, if a
symbol is not found in the imported modules.
The --resolve-calls
option should be used to compile programs that are only
based on IMPORT FGL
and no longer use the link phase.
For more details about the linker, see Linking programs.
Example
Module "account.4gl":
PRIVATE DEFINE current_account VARCHAR(20)
PUBLIC FUNCTION set_account(id)
DEFINE id VARCHAR(20)
LET current_account = id
END FUNCTION
Module "myutils.4gl":
PRIVATE DEFINE initialized BOOLEAN
PUBLIC TYPE t_prog_info RECORD
name STRING,
version STRING,
author STRING
END RECORD
PUBLIC FUNCTION init()
LET initialized = TRUE
END FUNCTION
PUBLIC FUNCTION fini()
LET initialized = FALSE
END FUNCTION
PUBLIC FUNCTION tokenize(s STRING,
a DYNAMIC ARRAY OF STRING)
DEFINE tok base.StringTokenizer,
x INTEGER
LET tok = base.StringTokenizer.create(s," \t\n")
CALL a.clear()
LET x=0
WHILE tok.hasMoreTokens()
LET x=x+1
LET a[x] = tok.nextToken()
END WHILE
END FUNCTION
Module "program.4gl":
IMPORT FGL myutils
IMPORT FGL account
DEFINE filename STRING
DEFINE proginfo t_prog_info -- Type is defined in myutils
MAIN
DEFINE arr DYNAMIC ARRAY OF STRING
LET proginfo.name = "program"
LET proginfo.version = "0.99"
LET proginfo.author = "scott"
CALL myutils.init() -- with module prefix
CALL set_account("CFX4559") -- without module prefix
CALL tokenize("aaa bbb ccc",arr)
DISPLAY arr[2]
END MAIN