[mercury-users] Problem with linking bigger project (Mercury and C++)

Ondrej Bojar oboj7042 at ss1000.ms.mff.cuni.cz
Sat Jan 5 23:57:56 AEDT 2002


Hi.

I'm still unable to link together lots of Mercury modules and C++ main
code. And it's so many files, so I do not like to send it all here. I
attach just the most important Mmakefile.

The goal:

subdirs:
lingv2/    ... all the mercury stuff
           ... here should all the mercury files be compiled and later the
           ... whole project linked
milan/     ... some subroutines in CPP, used by both, lingv2 and main
main/      ... the main program in CPP, in fact just a wrapper, infinite
           ... loop to call milan/ and lingv/ subroutines

Order of compilation:

cd milan; make all   # straightforward, make all .o files here, no problem
                     # with gcc

then

cd lingv2
mmake lingv_int.depend  # this is going to be the interface that CPP uses
          # to call my mercury routines
mmake lingv_int.o       # this creates lingv_mint.h as a side-effect, so
          # it is ready to use in main/
mmake ent.depend       # this is a wrapper module, so that the whole
          # project output file is to be called ent
mmake ent_init.o       # this is to prepare ent_init.h to be used in main/
          # when the main cpp code is ready to run mercury_init()

then

cd main
make testent.o    # this is the main infinite loop
make komplex.o    # this is the cpp-side of interface to my mercury stuff
                  # source files attached

And the final step:

cd ling2
mmake ent      # to link all .o files together

Fails with:

u-pl8:lingv2$mmake ent
ml --grade asm_fast.gc          -o ent ent_init.o \
        const.o csts.o csts2uttr.o czdcg.o czdcg_time.o czfs.o db_mint.o
debugstr.o dispatch.o dumbsgml.o ent.o fatal.o frame.o framemem.o fs.o fstag.o
fstagtab.o fstype.o globmem.o grammem.o handle.o hanproptab.o hear.o
hearsaytools.o konkr.o lexicon.o lexmem.o lingv_mint.o linmem.o list_tools.o mercdb.o
morfcat.o objmem.o obtain.o rowmem.o say.o sense.o stringutils.o termin.o ticks.o
unsafe.o uttr.o uttr2csts.o db_cint.o ../milan/db.o ../milan/seznam.o ../milan/veta.o
../milan/reakce.o ../main//komplex.o ../main//testent.o
ent_init.o: In function `mercury_init':
ent_init.o(.text+0x374): undefined reference to `<predicate 'main'/2 mode 0>'
collect2: ld returned 1 exit status
gmake: *** [ent] Error 1


The files are commented mostly in Czech, sorry. But the most important
part of the Mmake file is commented in English.

Thanks for help, Andrew.

On Sun, 16 Dec 2001, Fergus Henderson wrote:

> On 15-Dec-2001, Ondrej Bojar <oboj7042 at ss1000.ms.mff.cuni.cz> wrote:
> > When trying to link together a bigger project, I get lots of `undefined
> > reference' errors for functions/data objects such as:
> > MR_ticket_counter_var, MR_trail_ptr_var, MR_ticket_high_water_var,
> > MR_untrail_to...
>
> Without seeing the Mmakefile, it's a bit hard to be sure exactly what
> is going on, but my guess is that that error message is most likely
> because some part of your program was compiled with trailing enabled,
> but you didn't enable trailing when linking.  If you use the `--trail'
> option with Mmake, make sure that you put it in GRADEFLAGS rather than
> in MCFLAGS.
>
> > 1. Is the above "ml -o project ..." line correct? Do I miss anything?
>
> In an Mmakefile, rather than hard-coding `ml', you should generally use
> `$(ML) $(ALL_GRADEFLAGS) $(ALL_MLFLAGS)'.  This will include the setting
> of $(GRADEFLAGS), which is needed if you're building in a non-standard
> grade (e.g. with trailing enabled).
>
> > 2. Or can mmake do this for me *by itself*? (If I just specify c_code*.o
> > files somewhere.)
> > 3. Is there a better scheme to do all the compilations?
> > 4. Can anything save me the work of looking up all the necessary module*.o
> > files for linking? (The files are of course well known from
> > merc_interface.dep...)
>
> Yes to all three.
>
> If you set the `MLOBJS' variable to the c_code*.o files,
> then these files will be linked into any Mercury program.
>
> So the Mmakefile for your example could be written like this:
>
> 	MLOBJS = cpp_code1.o cpp_code2.o ... cpp_coden.o cpp_main.o
> 	C2INITFLAGS = --library
>
> 	MAIN_TARGET = mercury_interface
> 	depend: mercury_interface.depend
>
> If you want this to only apply to a particular program,
> then you can use `MLOBJS-<program>' rather than `MLOBJS':
>
> 	MLOBJS-mercury_interface = \
> 		cpp_code1.o cpp_code2.o ... cpp_coden.o cpp_main.o
> 	C2INITFLAGS-mercury_interface = --library
>
> 	MAIN_TARGET = mercury_interface
> 	depend: mercury_interface.depend
>
> These both have the minor drawback that the executable is named
> `mercury_interface' rather than `project'.  The simplest fix
> for that is to just add the following:
>
> 	project: mercury_interface
> 		cp mercury_interface project
>
> Another way of doing it is to have an empty top-level module
>
> 	:- module project.
> 	:- import_module mercury_interface.
>
> in the file project.m and then use
>
> 	MLOBJS-project = \
> 		cpp_code1.o cpp_code2.o ... cpp_coden.o cpp_main.o
> 	C2INITFLAGS-project = --library
>
> 	MAIN_TARGET = mercury_interface
> 	depend: mercury_interface.depend
>
>
-------------- next part --------------
#### The Mmakefile for all Mercury-compiled stuff
#### It's used twice in the whole compilation process
#### First, to make lingv_int.o and lingv_int_init.o
#### and as a side-effect their .h files
####  ie. the files needed to compile CPP-main code
#### Then somewhere else cppmain.o is compiled
#### And then this Mmakefile is used to mmake "ent"
####  ie. to link all .o files together

###### ENTIROOT
# Nyni se predopklada, ze make je spusten v ENTIROOT/src/ent/lingv2
ENTIROOT=../../..

###### Parametry kompilace
#GRADEFLAGS = --debug
CFLAGS = -I$(ENTIROOT)/src/shared/

# Propojeni se zdrojaky Milana
MILANDIR=../milan
MILANLIBS=$(MILANDIR)/db.o $(MILANDIR)/seznam.o $(MILANDIR)/veta.o $(MILANDIR)/reakce.o

# Where are the sources of main program stored:
# A separate Makefile is responsible of compiling the files, here they are just
# included for linking
main_subdir=../main/
main_objects=$(main_subdir)/komplex.o $(main_subdir)/testent.o

# Files needed for plain compilation of mercury-based stuff
# For other reasons, the mercury sources here do need some C stuff as well
#MLOBJS=db_cint.o $(MILANLIBS)

# Files needed for the final linking.
#MLOBJS-ent=db_cint.o $(MILANLIBS) $(main_objects)
# XXX Strangely, the line above *doesn't work*. When "mmake ent" is called,
# mmake *doesn't include* the $(main_objects) and includes just plain $(MLOBJS)
# I can fix this bug only by replacing both the MLOBJS lines with the next one
# just for the phase of linking:
MLOBJS=db_cint.o $(MILANLIBS) $(main_objects)

# This is to tell that "main" function should not be prepared by Mercury, but is
# included from somewhere else (testent.o)
C2INITFLAGS-ent= --library

# Mercury Compiler by umel vypsat statistiky o narocnosti kompilace
#MCFLAGS=-S

# Lze pridat -v, aby bylo videt, jak se presne MGNUC pousti
MGNUCFLAGS=-I $(MILANDIR) -v

# doplnuji ve snaze presvedcit Mercury, aby pracoval s C++
C2INITFLAGS-lingv_mint=--library


###### Pravidla, aby mmake umel kompilovat .cc na .o

.SUFFIXES: .cc
.cc.o:
	$(CXX) $(CXXFLAGS) -c $<

CXX=$(MGNUC) $(ALL_GRADEFLAGS) $(ALL_MGNUCFLAGS)

###### Pravidla pro preprocessing zdroju .pproc, .shan a .ordie, aby vznikaly .m
HANDLES=$(ENTIROOT)/src/conf/handles
DOHANDLES=$(ENTIROOT)/src/tools/dohandles.pl
DHFLAGS=--handles=$(HANDLES)

%.m: %.m.shan $(DOHANDLES) $(HANDLES)
	$(DOHANDLES) $(DHFLAGS) --quiet $*.m.shan
	mmake $*.depend

%.txt: %.txt.shan $(DOHANDLES) $(HANDLES)
	$(DOHANDLES) $(DHFLAGS) --quiet --decimal $*.txt.shan

%.m: %.ordie ordie.pl
	./ordie.pl $<

PPROC=./pproc
# preprocesor jako od Ccka, ale tak, aby vystup sezral i Mercury
%.m: %.m.pproc $(PPROC)
	$(PPROC) $*.m.pproc
	mmake $*.depend
%.vierrs: %.err vim_showerrors
	./maperrs.pl < $*.err > $*.vierrs
	vim -S vim_showerrors -c "cf $*.vierrs"


### Pomucka na ladeni

#testdb: konf_fak.txt

###### Makra pro programatora

.PHONY=sources
sources: files filterprintoutfiles.pl
	mmake `./files --suffix=m`
.PHONY=depend
depend: files filterprintoutfiles.pl
	mmake `./files --suffix=depend`

.PHONY=objects
objects: files filterprintoutfiles.pl
	mmake `./files --suffix=o`

#all: *.o

#allerrs: *.o
#	for f in *.o; do mmake $$f; done
#	mmake merrs

#init.o: lingv_mint_init.o init.o_files
#	mv lingv_mint_init.o init.o
#init.o_files: lingv_mint_init.c
#	sed -n -e "s/\*\* \([^.]*\)\.c/\1.o/p" lingv_mint_init.c > init.o_files

#lingv_mint_init.o: lingv_mint_init.c

#.PHONY: merrs
#merrs: maperrs.pl
	cat *.err | ./maperrs.pl > merrs

#depend: sources
#	mmake `ls *.m | sed -e 's/\.m/.depend/g'`

#tests:
#	mmake `ls test*.m | sed -e 's/\.m//g'`

#errors:
#	rm -f errors.log
#	find -maxdepth 1 -name "*.m" -exec makeerror {} \;
#	grep -v "OK" errors.log > errors.log.tmp
#	mv errors.log.tmp errors.log
#	find . -size 0 -exec rm -f {} \;
#fasterrors:
#	grep -v "OK" errors.log | cut -d: -f1 > tmp.fnames.fasterrors
#	rm -f errors.log
#	for fname in `cat tmp.fnames.fasterrors`; do makeerror $$fname; done
#	rm -f tmp.fnames.fasterrors
#
preds:
	grep "^:- pred\|^:- func" `ls *.m *.shan *.ordie *.pproc | sort | ./filterprintoutfiles.pl` | sed -e 's/::- /:/'

printout:
	ls *.m *.shan *.ordie *.pproc | sort | ./filterprintoutfiles.pl | grep -v "^test" > /tmp/files
	head -n 32000 `cat /tmp/files` | grep -v "^:- i" | grep -v ":- module" | cat -s >printout.txt

domu: homepack ziphomepack

SOUBORY_DOMU=*.out *.morfout *.err *.txt `ls *.m *.shan *.ordie *.pproc | ./filterprintoutfiles.pl`

homepack:
	rm -f printout.txt
	find . -size 0 -exec rm {} \;
	tar czf domu.tgz $(SOUBORY_DOMU)

ziphomepack:
	rm -f printout.txt
	find . -size 0 -exec rm {} \;
	rm -f domu.zip
	zip domu.zip $(SOUBORY_DOMU)

wc:
	wc `ls *.m *.shan *.ordie *.pproc | ./filterprintoutfiles.pl`

#remake: clean depend

#realremake: realclean depend

#runaway: realclean sources depend errors ziphomepack homepack

-------------- next part --------------
#include <string.h>

#include "reakce.h"
#include "komplex.h"

#include "db_cint.h"
/* MR_list + prevod na CVetu */



void cons_CVeta(CVeta &veta, int mluvci, int posluchac, int mood, MR_list sense);
void decons_CVeta(CVeta &veta, int &mluvci, int &posluchac, int &mood, MR_list &sense);

/* Z neznameho duvodu musim debilni strdup sam programovat!! */
char * strdup(const char * str);

TLinMem komplexne_inicializuj(int DBSTATE) {
    /* Inicializuje Mercury a pak Lingvistickou cast enta. */
    char dummy;
    TLinMem linmem;

    printf("komplexne_inicializuj: spoustim Mercury\n");

    /* Inicializace enginu Mercury */
    mercury_init(0, NULL, &dummy);
    /* parametry by mely mit vyznam: argc, argv, navratova hodnota pro kdovico,
     * ale my nastesti optiony nepotrebujeme
     */

    printf("komplexne_inicializuj: inicializuji lingvistiku, ziskavam prazdnou pamet.\n");

    /* Inicializace lingvistiky */
    linmem = lingv_init(DBSTATE);

    printf("komplexne_inicializuj: inicializovana lingv. pamet (%i)\n", linmem);

    return linmem;
}


char* komplexne_zpracuj(TLinMem inmem, TLinMem* outmem, char* input) {
    /* Tady Ondrej zavola poslechnuti stringu, obdrzi-li string, vrati ho,
     * obdrzi-li CVeta, necha ji zpracovat pomoci reakce_na_vetu, dostane
     * CVetu2 a tu necha druhou casti lingvistiky prevest na string.
     * Tedy v kazdem pripade vraci string.
     */

    TLinMem tempmem;
    char *output, *toutput;
    int status1, status2;
    int zaslech_mluvci, zaslech_posluchac, zaslech_mood;
    int vyslov_mluvci, vyslov_posluchac, vyslov_mood;
    MR_list vyslov_smysl;
    MR_list zaslech_sense;
    CVeta zaslech_veta, odpoved;

    status1 = lingv_hear(inmem, &tempmem, input, &zaslech_mluvci, &zaslech_posluchac, &zaslech_mood, &zaslech_sense, &output);
    if (status1 == 1) {
       /* lingvistika dokazala ve vete odhalit nejaky smysl! Tragedie!
        * Musime zavolat na zaslech_vetu reakci_na_vetu
        */ 
        cons_CVeta(zaslech_veta, zaslech_mluvci, zaslech_posluchac, zaslech_mood, zaslech_sense);
        /* doplnil jsem vse dulezite do struktury vety, ted ji nechme zpracovat */

        status2 = reakce_na_vetu(zaslech_veta, odpoved);
        /* status2 muze byt 1 ... ok, je pripravena odpoved
         *                  0 ... nechci odpovedet, chci mu rict, at tahne k certu
         *                 -1 ... interni chyba
         */
        switch (status2) {
        case 1:
          /* musime zase prevest vetu zpatky */
          decons_CVeta(odpoved, vyslov_mluvci, vyslov_posluchac, vyslov_mood, vyslov_smysl);
          toutput = lingv_say(tempmem, outmem, vyslov_mluvci, vyslov_posluchac, vyslov_mood, vyslov_smysl);
          output = strdup(toutput); /* tohle priserne neni zas tak priserne... viz nize */
          break;
        case 0:
          output = strdup("Nechci Ti odpovedet,  tahni k certu.");
          break;
        case -1:
          output = strdup("Velmi rad bych Ti odpovedel, ale nejak jsem se porouchal.");
        }
    }
    else {
        /* status1 == 0, poslechnuti vstup nepochopilo */
        output = strdup(output);  /* tohle priserne volani neni zas tak priserne
                                     puvodne retezec vlastnil Mercury, vyrobil ho, a jeho Garbage Collector
                                     ho taky zlikviduje. Jen my chceme mit tu kopii jiste vyrobenou, aby ji
                                     mihl Milan regulerne zrusit. */
        /* promenna output jiz je nastavena na to, cim se chci vymluvit. */
        *outmem = tempmem;
    }

    return output;
}


char* komplexne_vyslov(TLinMem inmem, TLinMem* outmem, CVeta & veta) {
    char *output, *toutput;
    int vyslov_mluvci, vyslov_posluchac, vyslov_mood;
    MR_list vyslov_smysl;
    
    decons_CVeta(veta, vyslov_mluvci, vyslov_posluchac, vyslov_mood, vyslov_smysl);
    output = lingv_say(inmem, outmem, vyslov_mluvci, vyslov_posluchac, vyslov_mood, vyslov_smysl);
    return strdup(output);
}

void cons_CVeta(CVeta &veta, int mluvci, int posluchac, int mood, MR_list sense) {
    MR_list_to_CHandlSeznam(sense, veta);
    /* prevedl jsem seznam handlu do vety */
    veta.Mluvci = mluvci;
    veta.Posluchac = posluchac;
    switch(mood) {
        case 0xa12300bf:  veta.TypVety = TV_OTAZKA; break;
        case 0xa12300be: veta.TypVety = TV_PRIKAZ; break;
        case 0xa12300bd:  veta.TypVety = TV_OZNAMENI; break;
    }
}

void decons_CVeta(CVeta &veta, int &mluvci, int &posluchac, int &mood, MR_list &sense) {
    CHandlSeznam_to_MR_list(veta, sense);
    mluvci = veta.Mluvci;
    posluchac = veta.Posluchac;
    switch (veta.TypVety) {
        case TV_OTAZKA:   mood = 0xa12300bf; break;
        case TV_PRIKAZ:   mood = 0xa12300be; break;
        case TV_OZNAMENI: mood = 0xa12300bd; break;
    }
}


/* Z neznameho duvodu musim debilni strdup sam programovat!! */
char * strdup(const char * str) {
    char *tmp;

    tmp = (char*) malloc(strlen(str)+1);
    strcpy(tmp, str);
    return tmp;
}

-------------- next part --------------
/* Komplexni prace s lingvistikou.
 * Ondrej Bojar
 *
 * Pro praci s lingvistickym modulem vam staci prave jen tento hlavickovy
 * soubor. Napred musite zavolat komplexne_inicializuj, to vam vrati
 * vzorovou prazdnou lingv. pamet (promenna typu TLinMem, velka jako int).
 * Pak pouzivate komplexne_zpracuj pro zpracovani vety, kterou jsme zaslechli
 * -- jako odpoved dostanete retezec nebo NULL. NULL znamena, ze nic nechci
 * rict.
 * Kdyz chcete neco vyslovit, pripravte to v podobe objektu CVeta a ten
 * predejte funkci komplexne_vyslov. Dostanete string, ktery se ma vyslovit.
 *
 * Funkce vyslov i zpracuj museji dostat aktualni lingvistickou pamet a 
 * na oplatku vrati novou lingvistickou pamet. K tomu slouzi prvni dva arg.
 */
#ifndef __KOMPLEX_H__
#define __KOMPLEX_H__

/* Pripojeni Milanovy databaze */
#include "db.h"
#include "veta.h"

/* Pripojeni mych predikatu pripravenych v lingv. */
#include "lingv_mint.h"

/* Pripojeni Mercuryovskeho enginu */
extern "C" {
#include "ent_init.h"
}

/* definice typu lingvisticke pameti, kdyz se s ni pracuje v C */
typedef MR_Word TLinMem;

TLinMem komplexne_inicializuj(int DBSTATE);
/* tato funkce inicializuje Mercury, vyrobi novou lingv. pamet a vrati ji.
 * Argument DBSTATE je ta navratova hodnota, kterou vydala inicializace
 * Milanovy databaze.
 */

char* komplexne_zpracuj(TLinMem inlinmem, TLinMem *outlinmem, char* input);

char* komplexne_vyslov(TLinMem inmem, TLinMem* outmem, CVeta & veta);


#endif
/* __KOMPLEX_H__ */


More information about the users mailing list