Quintus Prolog Manual
H-1: Stand-Alone Programs & Runtime Systems
Traditionally, the way to develop Prolog programs has been to compile all the sources into memory and then to create saved-states for stable portions of the program. The use of saved-states avoids the need to recompile unchanged code each time that a testing/debugging session is started. It can also be used as a way of packaging a completed application for later re-use or for use by others.
This chapter explains the use of tools which provide an alternative approach to program development: compiling and linking your sources to generate a stand-alone program. This is very much like the normal way of developing programs written in languages such as C.
The following terminology is used both here and in Chapter H-2.
H-1-1-2: Shared Libraries and Delivering Execuatables
WARNING: An application may depend upon shared libraries, but they are separate from the executable. So it is necessary to take explicit steps to ensure that they will in fact be present when the application is run on the target machine. They must be explicitly linked in to the deliverable as described in section H-2-5.
A stand-alone Prolog program is a single, standard, executable file which can be considered to be an extended version of the Development System, with your Prolog and foreign code being pre-loaded into it. This approach has several advantages as compared with the saved-state approach:
The disadvantage of linking, in comparison with using a saved state, is that the stand-alone program will require more disk space than a saved state, since it contains the Development Kernel and the user's foreign code.
Generating a stand-alone program is very much like generating a runtime system with the Quintus Prolog Runtime Generator. Exactly the same tools are used in each case, and the use of these tools is described in this chapter.
The purpose of the Runtime Generator is to provide a convenient and cost-effective way to distribute application programs to end-users. A runtime system differs from a stand-alone program in the following ways:
For information on the Runtime Generator see Chapter H-2.
H-1-1-5: Compiling and Linking
There are two major tools needed to convert a set of source files into a stand-alone program or a runtime system:
It is also possible to generate QOF files by saving them directly from a Prolog development system session, as described in Chapter G-5. QOF files saved in this way can then be used directly to build a stand-alone program with qld.
Figure H-1-1: Major steps in creating a stand-alone program
qpc allows independent compilation of the files which make up an application program. The dotted box in Figure H-1-1 illustrates this functionality.
The use of qpc and qld correspond to the use of the C compiler cc and the link-editor ld as follows:
Quintus Prolog C
sources: a.pl a.c
compiler: qpc cc
object files: a.qof a.o
linker: qld ld
executable: a.out a.out
H-1-1-6: The Runtime Kernel vs. Development Kernel
There is a command-line option, -D, that can be given to either qpc or qld to indicate that a stand-alone program rather than a runtime system is desired. This flag determines whether your program is to be linked with the Development Kernel or the Runtime Kernel. Each of these kernels consists of a QOF file and a .o file which together supply all the necessary support code for running Prolog. This support code includes memory management and the built-in predicates. The Development Kernel additionally contains support for Prolog development, such as the compiler and the debugger.
H-1-2: Invoking qpc, the Prolog-to-QOF Compiler
There are two main ways to invoke qpc:
qpc -c P1.pl ... Pn.pl (A) qpc [-D] [-o output-file] P1.pl ... Pn.pl (B)
(A). Invoking qpc with the -c option, means "compile to QOF and stop"; it simply produces a QOF file for each source file, as shown in H-1-1.
(B). Invoking qpc without specifying -c compiles all the sources to QOF and then calls qld to build an executable file corresponding to those sources. -D tells qld that the program is to be linked with the Development Kernel rather than the Runtime Kernel. -o specifies a name for the executable file to be generated by qld. Defaults to a.out. The -D and "-o output-file" options are passed on to qld if they were specified in the qpc command line.
NOTE: ".pl" extensions may be omitted in the qpc command line (provided that there is not another file with the same name and no extension.) Also, the Prolog files need not have ".pl" extensions. If a Prolog file does not have one, the name of the corresponding object format file is simply the name of the Prolog source file extended with ".qof". Otherwise, the name of the corresponding object format file is the name of the Prolog source file with the ".pl" extension replaced by ".qof". Source files may be specified as absolute or relative path names; each QOF file goes in the same directory as its source.
Further options allow you to run qpc in a verbose mode, to specify initialization files or add-ons, to customize the library search path, or make certain predicates invisible to the debugger.
For a summary of all the options to qpc refer to the Quintus Reference pages included in your distribution: type 'man qpc' to the UNIX prompt, or see Chapter N-1
H-1-3: Invoking qld, the QOF Link eDitor)
Figure H-1-2: Components of the qld Program
This diagram expands the qld "black box" in Figure H-1-1. QOF is a temporary QOF file, and OBJ is a temporary .o file.
H-1-3-1: Implicit invocation via qpc
When qpc is called without the -c command-line option, as in invocation (B), above, it first compiles all the specified Prolog files into QOF and then invokes qld as follows:
qld [-D] [-v] [-o output-file] -d qof-files object-files
When qld is called by qpc, it is called in the verbose mode (-v), with the following additional options:
-D determines that qld is to be linked with the Development Kernel rather than the Runtime Kernel. In either case two kernel files, one QOF and one .o file, must be linked in addition to the application files. (See figure H-1-2.)
-o specifies the name of the executable file which is to be the final product; defaults to a.out.
-d tells qld to link in any additional files on which any of the specified QOF files depends. See section H-1-4 for more details on file dependencies.
qof-files is a list of QOF files, P1.qof ... Pn.qof, the output of the qpc call.
object-files is a list of object (.o) files generated by compiling your foreign-language files with the appropriate compiler(s). These may include foreign libraries (eg., 'qpc -lX11').
In addition to the above arguments, qpc also passes to qld appropriate -f, -F and -L options if any non-default file search paths or library directories have been specified. The -f, -F and -L options have the same meaning for qld as they do for qpc; they are only meaningful when the -d option is specified, and they tell qld where to look for file specifications. See section H-1-5 for information on how qld makes use of file search paths and library directories.
If you wish to call qld with other options than those described in section H-1-3-1, use "qpc -c " and then call qld explicitly. The -c option to qpc causes qld to stop after generating a .o file, rather than continuing and calling ld. The .o file will be called "a.o" by default; this can be overridden with the -o option. Then you can make your own call to ld; this call must include any needed object-files and libraries.
One reason you might wish to do this is to avoid the use of shared object files and shared libraries in a runtime system which is to be delivered on a different machine. See section H-2-5 for more information on this and an example.
The steps taken by qld -- as illustrated in Figure H-1-2 -- are as follows:
cc [-v] [-o output-file] <runtime-directory>/qprel.o temp.o
object-files <runtime-directory>/libqp.a -lm
The file <runtime-directory>/qprel.o is the Development Kernel .o file. If you are linking to a Runtime Kernel "qprel.o" will be replaced by "qprte.o" in the above command. temp.o is the output
of Step 2. libqp.a is the Quintus C library.
For a complete summary of all the possible options to qld, type 'man qld', or see Chapter N-1.
H-1-4: Dependencies of QOF files
Each QOF file contains a record of all the files, including library files, upon which it depends, that is, for which its source file contains any of the following load commands:
:- compile(Files). :- ensure_loaded(Files). :- use_module(Files). :- use_module(File, ImportList). :- use_module(Module, File, ImportList). :- [Files]. :- reconsult(Files). :- load_foreign_executable(File). :- load_foreign_files(Files, Libraries). :- load_files (Files). :- load_files (Files, Options).
Each record is in the form which was used to specify the file in the load command: it may be a relative or absolute path name, or else it may be a file search path specification, such as:
H-1-4-1: Generating QOF Files and Dependencies
When Prolog files contain embedded commands to compile other files, each Prolog source file is compiled into a separate QOF file with one exception: if a module-file contains a command to load a non-module file, then the non-module file is compiled directly into the QOF file corresponding to the module-file. That is, there is no separate QOF file for a non-module file which is loaded into a module unless it is loaded into the default module 'user'. Each QOF file is written into the same directory as the corresponding Prolog source file.
Embedded ensure_loaded/1 and use_module/[1-3] commands also cause the specified files to be compiled unless there is a corresponding QOF file more recent than the corresponding Prolog source file. For example,
:- ensure_loaded(file).
causes file.pl to be compiled unless there is a file.qof more recent than the source. In the case where the QOF file is more recent than the .pl file, then the file is not compiled again. However, the QOF file's dependencies are checked and recompiled if not up to date.
file.pl:
:- ensure_loaded(library(basics)).
:- ensure_loaded(file1).
:- ensure_loaded(file2).
runtime_entry(start) :- go.
file1.pl:
< some foreign/[2,3] facts >
< some foreign_file/2 facts >
:- load_foreign_files([system(foreign)],[]).
% qpc file (A)Given the above files, the command (A) will have these results:
library-directory/basics.qof file1.qof file2.qof (B)
% qld -d file.qof (C)
If for some reason you didn't want to use the -d option to qld, you could achieve the same effect as "qpc file" by the following sequence of commands:
% qpc -c file file1 file2 % qld file.qof library-directory/basics.qof file1.qof file2.qof foreign.o
Alternatively, these commands would work:
% qpc -c file file1 file2 % qld file.qof "library(basics)" file1.qof file2.qof foreign.o
Note that moving a QOF file from one directory to another may render its dependencies incorrect, so that the -d option cannot be used when loading that file. If relative path names are used, a set of mutually dependent files can safely be moved around the directory hierarchy, or to another machine, provided that they retain the same positions relative to one another. In particular, a set of files which are all in the same directory can safely be moved. Using file search path specifications (see section H-1-5 and Chapter G-4) enables you to create alterable paths.
H-1-4-3: Using the UNIX make utility
The UNIX make utility may also be used to ensure that QOF files are up to date. For example, the following lines can be added to a make file to tell make(1) how to generate a QOF file from a .pl file.
# Quintus Prolog Compiler (qpc) section
.SUFFIXES: .qof .pl
QPC=qpc
QPCFLAGS=
.pl:
${QPC} $(QPCFLAGS) -o $ $<
.pl.qof:
${QPC} $(QPCFLAGS) -cN $<
H-1-5: File Search Paths and qld
When a directive such as (A), below, is encountered by qpc, it notes that the QOF file being produced has a dependency on basics.qof in the library. The dependency is not stored as an absolute path, so that when (B) is called (recall that the -d option causes qld to pull in all the dependencies of the specified QOF files), basics.qof will be sought wherever the current libraries are located. These need not be in the same place as at compilation time; in particular the QOF file may have been moved to a different machine.
:- ensure_loaded(library(basics)). (A) % qld -d ... (B)
The -L option of qpc and qld allow prepending library directory definitions to the already existing ones. The -f and -F options perform similar functions but are more flexible than -L: -f appends a file search path definition to the already existing set, while -F prepends a file search path. A -f option, as exemplified in (C), corresponds to a file_search_path/2 call, (D). In such calls, path can itself be a file search path, as in (E).
-f pathname:path (C)
file_search_path(pathname, path) (D)
-f "mypath:library(mypackage)" (E)
For more detail on these options, type 'man qpc' and 'man qld' or see Chapter N-1.
H-1-6: Embedded Commands and Initialization Files
This section discusses some differences that exist between compiling a file into QOF with qpc and compiling that file into memory using compile/1 under the Development System. In certain cases, if an application program was developed interactively using the built-in compiler, some changes may have to be made to the code before using qpc to compile it and link it with the Development Kernel or Runtime Kernel.
For example, if a file containing the following is compiled into memory, the embedded command will succeed after writing an "x" and a newline to the current output stream.
f(x). f(z). :- f(X), write(X), nl.Whereas, if the same file is given to qpc, a warning will be printed indicating that the embedded command failed. The reason for this is that when qpc compiles a Prolog file, it reads clauses from the source file one after the other and compiles them into a QOF file. The clauses for f/1 are not kept in memory, and the attempt to access them fails.
You do NOT need to read this section if both of the following are true.
H-1-6-1: Compile-time code vs. Runtime code
A Prolog program has up to three types of code in it:
When using the built-in compiler in the Development System, no distinction has to be made between the three types of code. They can coexist in one file. Before using qpc on a program, however, compile-time code must be separated out into its own file (or files). Then, to each file that needs this new file or files at compile time, add the goal (A) near the top of the file. This tells qpc to load NewFile directly into qpc before further compiling the current file. It does not include NewFile as a runtime dependency of the file. If you need NewFile to be loaded at compile time and also at runtime, use the goal (B) instead. This approach will work in the Development System as well as qpc.
:- load_files(NewFile, [when(compile_time)]). (A)
:- load_files(NewFile, [when(both)]). (B)
Alternatively, you may omit the use of load_files/2, instead specifying files to be loaded into qpc with the -i option. In this case, when you want to compile this file into the Development System, remember to first load the file(s) needed at compile time.
It is good programming style to use initialization/1 for goals to be activated at runtime. Note that predicates called as ":- Goal." need to be available at compile time, whereas predicates called as ":- initialization(Goal)." only need to be available at runtime.
qpc is implemented as a normal runtime system. Hence it has its own internal Prolog database. All compile-time code must be loaded into this database so that qpc can run it.
The -i command-line option is used to specify an initialization file. See the reference page for qpc(1).
H-1-6-3: Side-Effects in Compile-Time Code
One other way to add clauses to qpc's internal database is to assert them in an embedded command. For example, the sequence:
:- asserta(f(x)). :- f(X), write(X), nl.in a file given to qpc will work just as it would if the file were compiled into the Development System.
There are some problems with asserting clauses like this. One problem is that the asserted clauses will not be available at run-time. If the file had been loaded into the Development System, they would be available when the program was run.
Another problem arises if the compilation of one file depends on facts that are expected to be asserted into the database during the compilation of some other file. An approach of this sort may be useful in the Development System, but it is contradictory to the notion of independent compilation (see Figure H-1-1), which is one of the important features of qpc. This problem is not specific to asserting clauses; it arises with any compile-time side-effects that are intended to affect future compilation.
It is possible to avoid using separate compilation, by always recompiling your entire program every time any part of it is modified. It is still not generally safe to use compile-time side-effects in one file that affect the compilation of other files. This is because the order in which files are compiled is different in qpc. When qpc finds a command to compile a file, it looks in that file immediately to find out whether it is a module-file and if so what are its exports. But it does not actually compile the file immediately: it puts it on a queue to be compiled when the current file has been finished with. This is in contrast to compilation in the Development System, where embedded compile/1 commands are processed immediately as they are encountered.
Therefore, it is strongly recommended that side-effects in compile-time code be avoided, or at least restricted so that only the compilation of the current file is affected.
H-1-6-4: Modules and Embedded Commands
Embedded commands are called in the modules in which they are contained. Sometimes this seems strange, since the module is really a property of the program being compiled into QOF, and the embedded command is to be interpreted not with respect to that program, but rather with respect to the internal database of qpc.
H-1-6-5: Predicates Treated in a Special Way
While qpc is compiling Prolog source into QOF, certain built-in predicates are treated in a special way. Their behavior when used as embedded commands under qpc is different from their normal behavior. For example, (A) causes the file foo.pl to be compiled into .qof format, not, as you would expect from its normal meaning, into (qpc's) memory.
:- compile(foo). (A)
Similarly, if you define (B) in an initialization file, then the command (C) will cause foo to be compiled into QOF format after whatever goals you specified have been called.
my_compile(File) :- (B)
...{some goals}...,
compile(File).
:- my_compile(foo). (C)
The load_files/2 when option can be used to force a file to be loaded into memory at compile-time if so desired.
Note that the change of meaning of compile/1 etc does not apply during the loading of an initialization file, only while compilation to .qof format is taking place. Thus, if you put
:- my_compile(foo).in your initialization file (after the definition of my_compile/1), then this would mean compile foo.pl into memory.
The predicates following this behavior are:
Note that an embedded command of the form
:- compile(user).
will cause an error message from qpc. The same is true for specifying 'user' in embedded calls to consult/1 and similar commands, as well as in the command line of qpc. The reason for this restriction is to avoid possible confusion; under the Development System, giving 'user' as the argument to one of these predicates allows you to enter clauses directly from the terminal.
Clauses for the predicates foreign/[2,3] and foreign_file/2 are treated specially by qpc. They are always assumed to be compile-time predicates, to be used by a subsequent embedded load_foreign_executable/1 or load_foreign_files/2 goal. Therefore they are consulted into qpc's internal database rather than being compiled into QOF.
H-1-6-6: Restriction on Compile-Time Code
Since qpc is itself a runtime system, any code to be run at compile-time must obey the same restrictions as for any other runtime system. In particular, foreign code cannot be loaded into qpc with load_foreign_executable/1 or load_foreign_files/2. However, you can load QOF files into qpc, and if the QOF file has object file dependencies, they will be loaded also. For example, you might compile file.pl with qpc to get file.qof:
file.pl:
:- load_foreign_file(['prog1.o'], [])
:- load_foreign_executable(prog2.so)
file.qof:
... <dependency on prog1.o> ...
... <dependency on prog2.so> ...
In this case, file.pl and file.qof both depend on the same object files. However, while a runtime system can load file.qof, it cannot load file.pl, because load_foreign_executable/1 and load_foreign_files/2 are not available in the runtime kernel.
An operator declaration is a call to the predicate op/3 in an embedded command. See section G-1-4 for more information about operator declarations.
An operator declaration takes effect when it is encountered and remains in force during compilation of the file and during runtime as in the Development System.
H-1-8: Saved-States and QOF files
Saved-states may be created from within a stand-alone program or a runtime system in the normal way, using save_program/[1,2]. A saved-state may be restored, using the built-in predicate restore/1, or incrementally loaded using load_files/[1,2]. As discussed in Chapter G-5, saved-states are just QOF files, and there is complete flexibility in how they can be selectively saved and loaded. Saved states, and other selections of predicates and modules saved into QOF files, can also be directly used with qld to build a stand-alone program or runtime system.
Note that the restore/1 command is not as useful to load a saved-state (or any QOF file) into a runtime system as the load_files/[1,2] command. While a restore/1 in a stand-alone program, just as in the Development System, restarts the running executable and then loads the argument QOF file, a runtime system only restarts the executable, and the file, preceded by a +l flag, is passed to the application program which may elect to parse the arguments and then load the file. Similarly, if a saved-state is created from a runtime system and then restarted from the command line, the executable will be started, but the options list need to be parsed and the file loaded by the application program (see section L-3-153, and section G-3-1 or section N-1-1 for a description of the +l option).
H-1-9: Dynamic Foreign Interface
Runtime systems cannot dynamically load (additional) foreign code. However, they can load QOF files and if those have object file dependencies then the object files will be dynamically loaded at that time.
It is possible to dynamically load foreign code into a stand-alone program using load_foreign_executable/1 or load_foreign_files/2.
During development of applications involving both Prolog and foreign code, it can be very useful to build a stand-alone program which includes QUI. This way, you can use the QUI debugger for stepping through the Prolog code and a standard debugger such as dbx or dbxtool for stepping through the foreign code.
A good way to do this is to create a file qui.pl containing just the one line:
:- ensure_loaded(library(qui)).
This should then be compiled to QOF in the normal way and linked into your application. For example:
% qpc -D <application files> qui.pl
Then start up the resulting executable file using a command such as
% dbxtool a.out
See section I-3-11 for information about using a standard debugger in conjunction with a Prolog executable file.
NOTE: The reason for putting the ensure_loaded command in a file by itself, rather than including it in your application code, is that it will not work if executed in an ordinary "prolog" system that does not include QUI; it is not possible to load QUI dynamically.
contact:
product support
sales information