Quintus Prolog Manual


(PREV) (NEXT)

h-1: Creating Executables

h-1-1: Introduction

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.

h-1-1-1: Terminology

The following terminology is used both here and in {manual(h-2)}.

Kernel The code implementing Prolog's built-in predicates, plus support code such as memory management needed for running Prolog.

Runtime Kernel Provides only the core set of built-in predicates.

Development Kernel Provides the facilities of the Runtime Kernel, plus additional built-in predicates, the compiler, the debugger, and so on.

runtime system A Prolog application that has been linked with the Runtime Kernel; intended for delivery to end-users.

stand-alone program A Prolog program that has been linked with the Development Kernel; an extended version of the Development System.

static linking Linking a Prolog program to either the Development Kernel or the Runtime Kernel.

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 {manual(h-2-5)}.

h-1-1-3: Stand-Alone Programs

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:

1. Since a stand-alone program is a standard executable file, you can use standard source-level debugging tools such as dbx(1) to debug your foreign code at the source level.

2. Standard tools such as make(1) can be easily used to construct your program, ensuring that everything is up-to-date. This is particularly useful if you are using a lot of foreign code.

3. Startup is faster.

4. Your code (except for dynamic predicates) will be loaded into the text segment, so that it will be shared if the program is to be run by more than one process at a time on a single machine.

5. Different application programs can share compiled files. The format of compiled files, "Quintus Object Format" or "QOF", is portable across a number of different hardware types and operating systems.

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.

h-1-1-4: Runtime Systems

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 {manual(h-2)}. It is necessary to obtain a separate license to use the Runtime Generator.

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:

  1. the compiler, , which translates Prolog files into QOF files. This is described in {manual(h-1-1)}.
  2. the , , which links one or more QOF files together and generates an executable file. Often the user will not need to call directly because will do this automatically. The linker is described in {manual(h-1-2)}.

It is also possible to generate QOF files by saving them directly from a Prolog development system session, as described in {manual(g-5)}. QOF files saved in this way can then be used directly to build a stand-alone program with .

allows independent compilation of the files which make up an application program. The use of and 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:                                           cc
     object files:             a.qof                    a.o
     linker:                                             ld
     executable:               a.out                    a.out

h-1-1-6: The Runtime Kernel vs. Development Kernel

There is a command-line , , that can be given to either or 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 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 :

  P1.plPn.pl                                                              (A)

 [] [output-file] P1.plPn.pl                                              (B)

(A). Invoking with the option, means "compile to QOF and stop"; it simply produces a QOF file for each source file, as shown in h-0-1. (B). Invoking without specifying compiles all the sources to QOF and then calls to build an executable file corresponding to those sources. tells qld that the program is to be linked with the Development Kernel rather than the Runtime Kernel. specifies a name for the executable file to be generated by . Defaults to a.out. The and "output-file" s are passed on to if they were specified in the command line.

NOTE: ".pl" extensions may be omitted in the 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 s to refer to the Quintus Reference pages included in your distribution: type 'man qpc' to the UNIX prompt, or see {manual(n-1)}

h-1-3: Invoking qld, the QOF Link Editor

h-1-3-1: Implicit invocation via qpc

When is called without the command-line , as in invocation (B), above, it first compiles all the specified Prolog files into QOF and then invokes as follows:

 [] [] [output-file]  qof-files object-files

When is called by , it is called in the verbose mode (), with the following additional options: determines that is to be linked with the Development Kernel rather than the Runtime Kernel. In either case two kernel files, one QOF and one file, must be linked in addition to the application files. Specifies the name of the executable file which is to be the final product; defaults to a.out. tells to link in any additional files on which any of the specified QOF files depends. See {manual(h-1-3)} for more details on file dependencies. qof-files is a list of QOF files, P1.qof Pn.qof, the output of the call. object-files is a list of object () 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, also passes to appropriate -f, -F and s if any non-default file search paths or library directories have been specified. The -f, -F and s have the same meaning for as they do for ; they are only meaningful when the option is specified, and they tell where to look for file specifications. See {manual(h-1-4)} for information on how makes use of file search paths and library directories.

h-1-3-2: Explicit Invocation

If you wish to call with other s than those described in {manual(h-1-2-1)}, use "qpc -c " and then call explicitly. The to causes to stop after generating a file, rather than continuing and calling . The file will be called "a" by default; this can be overridden with the -o . Then you can make your own call to ; 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 {manual(h-2-5)} for more information on this and an example. The steps taken by are as follows:

  1. Link all the specified QOF files together with either the Runtime Kernel or Development Kernel. The result of this phase is a temporary QOF file.
  2. "Consolidate" the temporary QOF file into a temporary file using the subsidiary program .
  3. Call the UNIX C compiler, cc, to build an executable file. The form of the cc command, which is echoed to standard output if the is specified, is
  4. 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 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 s to , type 'man qld', or see {manual(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:

library(Name)
system(Name)
quintus(Name)
language(Name)
mypath(Name)

When the QOF file is passed to and the is specified, this information will be used to find all the QOF and object files on which this QOF file depends.

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.

h-1-4-2: Example

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)],[]).

%  file                                                                   (A)

Given the above files, the command (A) will have these results:

If for some reason you didn't want to use the to , you could achieve the same effect as " file" by the following sequence of commands:

%   file file1 file2
%  file.qof library-directory/basics.qof file1.qof file2.qof foreign.o

Alternatively, these commands would work:

%   file file1 file2
%  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 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 {manual(h-1-4)} and {manual(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 , 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 causes 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)

%   ...                                                                   (B)

The of and allow prepending library directory definitions to the already existing ones. The -f and -F s perform similar functions but are more flexible than : -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 {manual(n-1)}.

h-1-6: Embedded Commands and Initialization Files

This section discusses some differences that exist between compiling a file into QOF with 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 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 curre output stream.

f(x).
f(z).

:- f(X), write(X), nl.

Whereas, if the same file is given to , a warning will be printed indicating that the embedded command failed. The reason for this is that when 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.

  1. The only embedded commands that you use in your Prolog files are commands to load other files, that is, commands from the list at the beginning of {manual(h-1-3)}.
  2. You do not use term_expansion/2 to transform your source code at compile-time.

h-1-6-1: Compile-time code vs. Runtime code

A Prolog program has up to three types of code in it:

  1. code which implements the application;
  2. code which helps compile the application, and which is not used during the execution of the application;
  3. code which is necessary to both the execution of the application and its compilation.

The first type of code is the normal case and may be referred to as runtime code, since it is intended to be executed when the application is run. The second type of code is called compile-time code. Any predicates which are to be called from embedded commands are examples of compile-time code. The other main use of compile-time code is in the definition of term_expansion/2 which allows you to specify arbitrary transformations to be done by the compiler as it reads in clauses. See the reference page for more information on term_expansion/2. 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 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 to load NewFile directly into 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 .

    :- 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 with the option. In this case, when you want to compile this file into the Development System, remember to first load the file(s) needed a 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.

h-1-6-2: Initialization Files

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 can run it. The command-line 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 '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 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, 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 . When 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 interprete not with respect to that program, but rather with respect to the internal database of .

h-1-6-5: Predicates Treated in a Special Way

While is compiling Prolog source into QOF, certain built-in predicates are treated in a special way. Their behavior when used as embedded commands under 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 ('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:

compile/1
compile files
consult/1
compile files
load_files/[1,2]
compile files
ensure_loaded/1
compile files
load_foreign_files/2
compile links to foreign code
load_foreign_executable/1
compile links to foreign code"
no_style_check/1
disable style checking
op/3
declare operator(s) (see {manual(h-1-6)})
reconsult/1
compile files
style_check/1
enable style checking
use_module/[1-3]
compile module-files
./2
(usually written [Files]) compile files

Note that an embedded command of the form

:- compile(user).

will cause an error message from . The same is true for specifying 'user' in embedded calls to consult/1 and similar commands, as well as in the command line of . 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 . 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 's internal database rather than being compiled into QOF.

h-1-6-6: Restriction on Compile-Time Code

Since 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 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.

h-1-7: Operator Declarations

An operator declaration is a call to the predicate op/3 in an embedded command. See {manual(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 {manual(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 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 {manual(l-3)}, and {manual(g-3-1)} or {manual(n-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.

h-1-10: Linking with QUI

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 {manual(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.


Copyright (C) 1997 AI International Ltd
contact: product support sales information