11 Jun, 2006, Guest wrote in the 1st comment:
Votes: 0
Got me a bit of a stumper here. Not sure exactly what I'm doing wrong, but I have to assume something really boneheaded and stupid. The gist of it is, I want to be able to load commands as shared objects, like DLL files in Windows for lack of a better comparision ( and because I'm not sure how else to describe it ).

The Makefile for my codebase in general, right at the moment, is:
#This is the AFKMud Makefile.
#This should be usable on pretty much any Linux system.
#Refer to your 'man gcc' for explanations of these options
#To compile in FreeBSD or OpenBSD, use the gmake compiler.

#Used for Alsherok stuff, uncommenting it should have no affect, though why would you want to?
ALSHEROK = 1

#Change this to reflect whichever compiler you want to use. Take off the ccache part if you get errors about it being missing.
CC = g++

#Type of machine to compile for. Athlon works just as well on Duron too.
#The march option needs to match the general family of CPU.
#If you don't know what to set for these options, and your system administrator doesn't either, comment this line out
MACHINE = -march=athlon-xp

#Uncomment if compiling in Windows under Cygwin
#CYGWIN = 1

# Uncomment the two lines below if compiling on a Solaris box
#SOLARIS_FLAG = -Dsun -DSYSV -Wno-char-subscripts
#SOLARIS_LINK = -lnsl -lsocket -lresolv

#Uncomment the line below if you are getting undefined references to dlsym, dlopen, and dlclose.
#Comment it out if you get errors about ldl not being found.
NEED_DL = -ldl

#Some systems need this for dynamic linking to work.
EXPORT_SYMBOLS = -export-dynamic

#Math functions - uncomment if you get errors or warnings about math functions.
MATH_LIB = -lm

#IMC2 - Comment out to disable IMC2 support
IMC = 1

#Internal Web Server - comment out to disable web server code.
WEB = 1

#Multiport support. Comment out to disable this feature.
MULTIPORT = 1

#Miscellaneous compiler options.
OPT_FLAG = -pipe -Os
DEBUG_FLAG = -g2
#PROF_FLAG = -pg

W_FLAGS = -Wall -Wextra -Wformat=2 -Wshadow -Wpointer-arith -Wcast-align -Wcast-qual -Wredundant-decls -Wconversion

ifndef CYGWIN
W_FLAGS := -Werror
endif

C_FLAGS = $(MACHINE) $(W_FLAGS) $(DEBUG_FLAG) $(OPT_FLAG) $(PROF_FLAG) $(SOLARIS_FLAG) $(EXPORT_SYMBOLS)
L_FLAGS = $(MACHINE) $(DEBUG_FLAG) $(OPT_FLAG) $(PROF_FLAG) $(EXPORT_SYMBOLS) $(SOLARIS_LINK) $(MATH_LIB) -lz -lgd $(NEED_DL)
#D_FLAGS : For the DNS Resolver process. No need in linking all the extra libs for this.
D_FLAGS = $(MACHINE) $(W_FLAGS) $(DEBUG_FLAG) $(OPT_FLAG) $(PROF_FLAG) $(SOLARIS_LINK)

C_FILES = act_comm.c act_info.c act_move.c act_obj.c act_wiz.c \
archery.c area.c areaconvert.c auction.c ban.c bits.c boards.c \
build.c calendar.c channels.c character.c clans.c color.c \
comm.c commands.c comments.c connhist.c const.c db.c deity.c descriptor.c \
editor.c environment.c event.c event_handler.c features.c \
fight.c finger.c handler.c hashstr.c help.c hotboot.c \
imm_host.c liquids.c magic.c mapout.c mapper.c md5.c \
misc.c mobindex.c mspecial.c mudcfg.c mud_comm.c mud_prog.c \
new_auth.c object.c objindex.c olcmob.c olcobj.c olcroom.c overland.c \
pfiles.c player.c polymorph.c rare.c renumber.c reset.c \
roomindex.c save.c sha256.c ships.c shops.c skills.c skyship.c slay.c \
tables.c track.c treasure.c update.c

ifdef WEB
C_FILES := websvr.c $(C_FILES)
C_FLAGS := $(C_FLAGS) -DWEBSVR
endif

ifdef IMC
C_FILES := imc.c $(C_FILES)
C_FLAGS := $(C_FLAGS) -DIMC
endif

#Our specific stuff, see to it this stays out of the AFKMud Makefile!
#And anyone outside of Alsherok who sees this will get errors.
#If this happens, Samson wasn't paying attention to what he was doing.
ifdef ALSHEROK
C_FILES := apc.c $(C_FILES)
endif

ifdef MULTIPORT
C_FILES := shell.c $(C_FILES)
C_FLAGS := $(C_FLAGS) -DMULTIPORT
#Sorry folks, this doesn't work in Cygwin
ifndef CYGWIN
C_FILES := mudmsg.c $(C_FILES)
endif
endif

O_FILES := $(patsubst %.c,o/%.o,$(C_FILES))

H_FILES = $(wildcard *.h)

ifdef CYGWIN
AFKMUD = afkmud.exe
RESOLVER = resolver.exe
else
AFKMUD = afkmud
RESOLVER = resolver
endif

all:
@echo "Building AFKMud….";
$(MAKE) -s afkmud
@echo "Buidling DNS Resolver…";
$(MAKE) -s resolver
@echo "Building command modules…";
$(MAKE) -C cmd

# pull in dependency info for *existing* .o files
-include dependencies.d

afkmud: $(O_FILES)
@rm -f $(AFKMUD)
ifdef CYGWIN
@dlltool –export-all –output-def afkmud.def $(O_FILES)
@dlltool –dllname $(AFKMUD) –output-exp afkmud.exp –def afkmud.def
$(CC) -o $(AFKMUD) $(O_FILES) afkmud.exp $(L_FLAGS)
else
$(CC) -o $(AFKMUD) $(O_FILES) $(L_FLAGS)
endif
@echo "Generating dependency file …";
@$(CC) -MM $(C_FLAGS) $(C_FILES) > dependencies.d
@perl -pi -e 's.^([a-z]).o/$$1.g' dependencies.d
@echo "Done building AFKMud.";
@chmod g+w $(AFKMUD)
@chmod a+x $(AFKMUD)
@chmod g+w $(O_FILES)

indent:
@indent -ts3 -nut -nsaf -nsai -nsaw -npcs -npsl -ncs -nbc -bls -prs -bap -cbi0 -cli3 -bli0 -l125 -lp -i3 -cdb -c1 -cd1 -sc -pmt $(C_FILES) resolver.c
@indent -ts3 -nut -nsaf -nsai -nsaw -npcs -npsl -ncs -nbc -bls -prs -bap -cbi0 -cli3 -bli0 -l125 -lp -i3 -cdb -c1 -cd1 -sc -pmt $(H_FILES)

indentclean:
@rm *.c~ *.h~

resolver: resolver.o
@rm -f $(RESOLVER)
$(CC) $(D_FLAGS) -o $(RESOLVER) resolver.o
@echo "Done buidling DNS Resolver.";
@chmod g+w $(RESOLVER)
@chmod a+x $(RESOLVER)
@chmod g+w resolver.o

clean:
@rm -f o/*.o $(AFKMUD) afkmud.def afkmud.exp core dependencies.d $(RESOLVER) resolver.o
$(MAKE) all

purge:
@rm -f o/*.o $(AFKMUD) afkmud.def afkmud.exp core dependencies.d $(RESOLVER) resolver.o

o/%.o: %.c
@echo " Compiling $@….";
$(CC) -c $(C_FLAGS) $< -o $@

.c.o: mud.h
$(CC) -c $(C_FLAGS) $<


Yes, it looks weird and yes it can probably be done better. But. It works. The important part to note is the "command modules" part, which calls a nested Makefile down in the src/cmd directory:

# 
# Module Makefile
# Written by Chris Fewtrell 20021008
#
# This Makefile allows for the creation to any shared lib file
# for any do_func, attempts to "make do_func" where do_func.c is
# a extistant file will result in the .c processed to a .o
# and the .o converted to a .so and placed ready to be loaded
# by the system. Lack of a .c will result in a Make error.
#
# This should allow for easiest per-module make from inside
# the mud using either a fork()'ed process or thread.
# Before going into production, the "all" target should be
# removed, as this can be time consuming and affect existing
# loaded modules
#

O_FILEDIR := ./o/
SO_FILEDIR := ./so/

C_FILES := $(wildcard *.c)
SO_FILES := $(patsubst %.c,%.so,$(C_FILES))
O_FILES := $(patsubst %.so,$(O_FILEDIR)%.o,$(SO_FILES))
TARGETS := $(patsubst %.c,%,$(C_FILES))

all: $(TARGETS)

clean: cleano
@echo Removing All Shared Library files
@-rm -f $(patsubst %.so,$(SO_FILEDIR)%.so,$(SO_FILES))
@echo Clean Complete!

cleano:
@echo Removing All Object Files
@-rm -f $(O_FILES)

remakeall: clean all

$(O_FILES): $(O_FILEDIR)%.o : %.c
@echo Compiling $(patsubst %.c,%,$<)
-g++ -g3 -fpic -c -export-dynamic -Wall -Os -o $@ $<

$(SO_FILES): %.so : $(O_FILEDIR)%.o
-g++ -g3 -shared -Wl,-soname,$(patsubst %o,%,$<) -o $(SO_FILEDIR)$@ $<

$(TARGETS): do_% : do_%.so


This one isn't my own doing. Chris gave me this Makefile long ago and I just never got around to using it for anything until tonite. It appears to try working, but dies like so:

Quote
[samson@boralis: ~/Alsherok/src] make
Building AFKMud….
make -s afkmud
Buidling DNS Resolver…
make -s resolver
Building command modules…
make -C cmd
make[1]: Entering directory `/home/samson/Alsherok/src/cmd'
g++ -g3 -shared -Wl,-soname,o/do_test. -o ./so/do_test.so o/do_test.o
cc do_test.c do_test.so -o do_test
cc: do_test.so: No such file or directory
In file included from do_test.c:1:
../mud.h:32:18: error: vector: No such file or directory
../mud.h:33:16: error: list: No such file or directory
../mud.h:34:15: error: map: No such file or directory
../mud.h:35:18: error: bitset: No such file or directory
In file included from do_test.c:1:
../mud.h:37: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'namespace'
../mud.h:62: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'descriptor_data'
../mud.h:63: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'area_data'
../mud.h:64: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'mob_index'
../mud.h:65: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'obj_index'
../mud.h:66: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'room_index'
../mud.h:67: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'char_data'
../mud.h:68: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'pc_data'
../mud.h:69: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'obj_data'


It goes on for another 2 screens full of stupidness like that. Here's the do_test source file:
#include "../mud.h"

CMDF( do_test )
{
ch->print( "If you see this, I guess it worked.\r\n" );
return;
}


Short, sweet, to the point. The include as listed there apparently works but I can't figure out why it's then telling me that the stuff in the mud.h file can't be located. This is what's at the top portion of mud.h, which compiles just fine for the regular code:
#ifndef __MUD_H__
#define __MUD_H__

#include <vector>
#include <list>
#include <map>
#include <bitset>


Currently all of my do_fun code works fine except for do_test which is what I was experminenting with before doing major damage… er… work on the codebase. Anyone have any thoughts as to where I botched this up?

btw, I posted this here because although my specific problem is with AFKMud, this is a more general thing that others who want to do this might run into.
11 Jun, 2006, Guest wrote in the 2nd comment:
Votes: 0
Scrapped the secondary Makefile and came up with this instead:
#AFKMud dynamically loaded commands.
#This makefile can be invoked directly, or via the makefile in the src directory.
#
#Takes any existing C files in this directory and makes dynamically loadable .so files out of them.

C_FILES := $(wildcard *.c)
SO_FILES := $(patsubst %.c,so/%.so,$(C_FILES))

all: $(SO_FILES)
@echo "Done building modular commands.";

clean:
@rm -f so/*.so
$(MAKE) all

purge:
@rm -f so/*.so

so/%.so: %.c
@echo " Compiling $@….";
g++ -c -g2 -shared $< -o $@


The files all compile now. But whenever trying to load one of the modules, I get the following back as an error:

../src/cmd/so/do_test.so: only ET_DYN and ET_EXEC can be loaded

Bit stuck here as I don't know what that means and Google has been less than helpful in finding a solution.
11 Jun, 2006, Guest wrote in the 3rd comment:
Votes: 0
The following Makefile:
#AFKMud dynamically loaded commands.
#This makefile can be invoked directly, or via the makefile in the src directory.
#
#Takes any existing cpp files in this directory and makes dynamically loadable .so files out of them.

#Type of machine to compile for. Athlon works just as well on Duron too.
#The march option needs to match the general family of CPU.
#If you don't know what to set for these options, and your system administrator doesn't either, comment this line out
MACHINE = -march=athlon-xp

# Uncomment the two lines below if compiling on a Solaris box
#SOLARIS_FLAG = -Dsun -DSYSV -Wno-char-subscripts
#SOLARIS_LINK = -lnsl -lsocket -lresolv

#IMC2 - Comment out to disable IMC2 support
IMC = 1

#Internal Web Server - comment out to disable web server code.
WEB = 1

#Multiport support. Comment out to disable this feature.
MULTIPORT = 1

#Miscellaneous compiler options.
OPT_FLAG = -fpic -pipe -Os
DEBUG_FLAG = -g2
#PROF_FLAG = -pg

W_FLAGS = -Wall -Wextra -Wformat=2 -Wshadow -Wpointer-arith -Wcast-align -Wcast-qual -Wredundant-decls -Wconversion

C_FLAGS = $(MACHINE) $(W_FLAGS) $(DEBUG_FLAG) $(OPT_FLAG) $(PROF_FLAG) $(SOLARIS_FLAG)
C_FILES := $(wildcard *.cpp)
O_FILES := $(patsubst %.cpp,o/%.o,$(C_FILES))
SO_FILES := $(patsubst o/%.o,so/%.so,$(O_FILES))

ifdef WEB
C_FLAGS := $(C_FLAGS) -DWEBSVR
endif

ifdef IMC
C_FLAGS := $(C_FLAGS) -DIMC
endif

ifdef MULTIPORT
C_FLAGS := $(C_FLAGS) -DMULTIPORT
endif

all:
$(MAKE) -s obj
$(MAKE) sobj
@echo "Done building modular commands.";

obj: $(O_FILES)

sobj: $(SO_FILES)

clean:
@rm -f o/*.o so/*.so
$(MAKE) all

purge:
@rm -f o/*.o so/*.so

o/%.o: %.cpp
@echo " Compiling $@….";
g++ -c $(C_FLAGS) $< -o $@

so/%.so: %.o
@echo " Linking $@….";
g++ -shared $< -o $@


Produces the following error:
Quote
[samson@boralis: ~/Alsherok/src/cmd] make clean
make all
make[1]: Entering directory `/home/samson/Alsherok/src/cmd'
make -s obj
make[2]: Entering directory `/home/samson/Alsherok/src/cmd'
Compiling o/do_aexit.o….
Compiling o/do_areas.o….
Compiling o/do_climate.o….
Compiling o/do_score.o….
make[2]: Leaving directory `/home/samson/Alsherok/src/cmd'
make sobj
make[2]: Entering directory `/home/samson/Alsherok/src/cmd'
g++ -c -o do_aexit.o do_aexit.cpp
Linking so/do_aexit.so….
g++ -shared do_aexit.o -o so/do_aexit.so
g++ -c -o do_areas.o do_areas.cpp
Linking so/do_areas.so….
g++ -shared do_areas.o -o so/do_areas.so
g++ -c -o do_climate.o do_climate.cpp
Linking so/do_climate.so….
g++ -shared do_climate.o -o so/do_climate.so
g++ -c -o do_score.o do_score.cpp
do_score.cpp: In function 'void do_score(char_data*, char*)':
do_score.cpp:23: error: 'class pc_data' has no member named 'imcchardata'
make[2]: *** [do_score.o] Error 1
rm do_areas.o do_climate.o do_aexit.o
make[2]: Leaving directory `/home/samson/Alsherok/src/cmd'
make[1]: *** [all] Error 2
make[1]: Leaving directory `/home/samson/Alsherok/src/cmd'
make: *** [clean] Error 2
[samson@boralis: ~/Alsherok/src/cmd]


The command:
Quote
g++ -c -o do_score.o do_score.cpp


Should not be coming into this, and I can't figure out why it is. Anyone have any ideas?
31 Dec, 2007, Kline wrote in the 4th comment:
Votes: 0
I turned all of the core elements for classes in my MUD (God Wars based) into shared object files awhile back to facilitate just re-compiling a single file, a quick reload command in-game, and all is updated. Really cut down on the amount of copyovers I do that disrupt players.

Not sure if I have the most eloquent way of doing it, but it works :)
C_FLAGS = -Wall -g $(DBFLAG) $(PROF)
L_FLAGS = -lz -lpthread -lcrypt -ldl -rdynamic -L/usr/include/mysql -lmysqlclient $(PROF)


Should just need the -ldl -rdynamic for your objects. This is for the actual game binary compile/linking.

SO_I = -g -c -fpic $(DBFLAG) $(PROF)
SO_O = -shared -lm $(PROF) -o


That is how I compile my object files. Example:

$(CC) $(SO_I) sociallib.c
$(CC) $(SO_O) sociallib.so sociallib.o
$(CC) $(SO_I) helplib.c
$(CC) $(SO_O) helplib.so helplib.o


Again, gotta be a better way of doing this, but hey, it works, right? :)

Loading libs at boot:
if( (helplib = dlopen(HELP_LIB, RTLD_LAZY | RTLD_GLOBAL)) == NULL)
{
sprintf(log_buf,"%s",dlerror());
log_string(log_buf);
log_buf[0] = '\0';
}
else
lib_count++;


And re-loading on the fly:
dlclose(helplib);
helplib = dlopen(HELP_LIB,RTLD_LAZY | RTLD_GLOBAL);


Let me know if you need anything else or have questions :)
Also, to anybody, if you have a slicker way of doing this, please share :D Mine works but is so ugly in the Makefile :(
01 Jan, 2008, Guest wrote in the 5th comment:
Votes: 0
Wow. I forgot I even posted this. From what I can tell in my code now this appears to be working. I'll have to try it again when I get a chance though since I don't even remember how I was handling the compiling of command modules.

I was actually attempting to make it more than just isolated commands that get treated this way, but this was a first step. What I'd really like to accomplish with it is to be able to make things like IMC into a plugin rather than having to toggle it on/off via the Makefile. Since IMC has more than just DO_FUN commands to consider, I got stuck trying to figure out how to be able to call on the code from a plugin version of it when it's there, and ignore those same calls when it's not.
01 Jan, 2008, Kline wrote in the 6th comment:
Votes: 0
I didn't even realize the thread age before I replied, just bored late one night, haha…

A lot of it could be handled pretty easy with dlsym.

void func(args)
{
void (*handle)()

if( (handle = dlsym(yourlib,"your_func")) == NULL )
regular_func();
else
(*handle)(ch);
}

That's how I do all of mine, except for sptting a dlerror() if NULL instead of a different function. Handle just needs to be void, int, bool, w/e the function it calls in the library actually is.
0.0/6