1
votes

I'm designing my first C++ program but I'm having a hard time understanding how to install and use a third party library called pugixml.

Here is my structure:

  • root/Makefile
  • root/src/main.cpp
  • root/include/pugi/pugixml.hpp
  • root/include/pugi/pugixml.cpp
  • root/include/pugi/pugiconfig.hpp

I believe I was meant to dump all the pugixml in a folder called pug in my includes folder and then refer to it from my Makefile like this;

CC = g++
CFLAGS = -g -Wall -std=c++11
SRC = src
INCLUDES = include
TARGET = main

all: $(TARGET)

$(TARGET): $(SRC)/$(TARGET).cpp pugi/pugixml.cpp
    $(CC) $(CFLAGS) -I $(INCLUDES) -o $@ $^ 

clean:
    $(RM) $(TARGET)

However when I run the Makefile or get this error:

main in main-bf0b72.o
  "pugi::xml_node::children(char const*) const", referenced from:
      _main in main-bf0b72.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [main] Error 1

This I've been told is due to thee fact that I have put the pugi cpp file in includes.

Question

  • Where should the various pugi files go, if not where I have put them?
  • How should I modify my Makefile accordingly?

Code Edit

#include "pugi/pugixml.hpp"

#include <iostream>
#include <string>
#include <map>

int main() {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry"))
        const char* idcs = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }
4
Don´t you think your first C++ program should be something without third-party libs? Not necessarily because you can´t write it, but eg. to test if your compiler is installed correctly and works as expected.deviantfan
@deviantfan I've done a hello world but then I thought I'd jump in at the deep end, so technically this is my second.Jimmy
We can't help you without seeing the code. At least portions that include your header.Francisco Aguilera
Also, if you're building the "pugi" sources you should just put all of the source and header files in the same directory and forget the -I include statement. Or, consider building them into a library and then linking such library to your executable.Francisco Aguilera
@FranciscoAguilera I've added the code if that helpsJimmy

4 Answers

3
votes

There are a multitude of locations in different platforms where headers and libraries are stored.

In unix systems, typically, headers for a user will be stored in:

/usr/local/include

Shared and static libraries would be stored in:

/usr/local/lib

In Windows, headers and libraries are usually stored within an installed program's directory.

C:\Program Files\SomeProgram\

C:\Program Files(x86)\SomeProgram\

For these reasons, there exist a number of cross-platform configuration tools. Notably, CMake, which will generate a platform independent "Makefile" to build, link, and install your libraries/executables.

In your case, you have two options.

  1. You can do a static build of the pugixml library, which shouldn't be a problem, since it is lightweight.
  2. You can build a dynamic library, and then link your executable to it.

For option 1, easiest would be to place the source and include files all under one src directory. Your directory structure would look like this:

Root
|--Makefile
|--bin
|--src
|  --main.cpp
|  --pugixml.cpp
|  --pugiconfig.hpp
|  --pugixml.hpp
--

Your Makefile would look like this:

# An explanation of this Makefile can be found here: http://stackoverflow.com/questions/5098798/could-someone-explain-this-make-file

# Compiler
CC = g++
CFLAGS = -Wall -g -std=c++11

# Linker
LDFLAGS =

# Libraries
LIBS =

# Directories
BINDIR = bin
SRCDIR = src

# Files
SRCS = $(shell find $(SRCDIR) -name '*.cpp')
OBJS = $(patsubst $(SRCDIR)/%.cpp, $(BINDIR)/%.o, $(SRCS))
EXEC = $(BINDIR)/main

all: $(SRCS) $(EXEC)

$(EXEC): $(OBJS)
    $(CC) $(LDFLAGS) $(OBJS) -o $@

$(BINDIR)/%.o: $(SRCDIR)/%.cpp
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    /bin/rm -f $(OBJS) $(EXEC)

Main.cpp

#include <iostream>
#include <string>
#include <map>

#include "pugixml.hpp"

int main(int argc, char * argv[]) {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }
}

This is really the equivalent of building and linking a pugixml static library with your project, with the downside that you won't have said library readily available to link with other programs.

You wouldn't need to worry about custom include locations -I in your makefile at all and, since, from the looks of it, you aren't writing a library, there wouldn't be a need for a complex directory structure with include and lib directories, etc.

Option 2 would be if you are writing both an executable and library which are meant to be distributed together. For this case, and in all cases, really, I would recommend you use Cmake or another configurable utility. Your project structure would look like this:

Root
|--CMakeLists.txt
|--bin
|--build
|--src
|  --CMakeLists.txt
|  --main.cpp
|--lib
|  --CMakeLists.txt
|  --pugixml.cpp
|--include
|  --pugiconfig.hpp
|  --pugixml.hpp
--

CMakeLists.txt

project(main CXX)
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)

subdirs(src)
subdirs(lib)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)

set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_BUILD_TYPE Debug)

src/CMakeLists.txt

include_directories(${main_SOURCE_DIR}/include)

file(GLOB SRC_FILES *.cpp)
add_executable(main ${SRC_FILES})

target_link_libraries(main pugixml)

set_property(TARGET main PROPERTY CXX_STANDARD 11)
set_property(TARGET main PROPERTY CXX_STANDARD_REQUIRED ON)

lib/CMakeLists.txt

include_directories(${main_SOURCE_DIR}/include)

file(GLOB SRC_FILES *.cpp)
add_library(pugixml SHARED ${SRC_FILES})

set_property(TARGET pugixml PROPERTY CXX_STANDARD 11)
set_property(TARGET pugixml PROPERTY CXX_STANDARD_REQUIRED ON)

Main.cpp

#include <iostream>
#include <string>
#include <map>

#include "pugixml.hpp"

int main(int argc, char * argv[]) {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }
}

And building your project now is even easier, and truly cross-platform, as you don't need to worry about writing a Makefile. CMake will generate a platform-independent utility for you!

cd build
cmake ..
make

If you go to the bin folder you will find the generated executable and shared library. You can run/distribute them.

You could also compile your executable with a static version of the library by changing lib/CMakeLists.txt SHARED to STATIC, however, this is basically equivalent as I mentioned earlier, to the static compilation of option 1.

You can also run make install to have your binary installed to /usr/local/bin and library installed to /usr/local/lib. You can then execute your binary from the terminal by typing main anywhere in your system (assuming your have /usr/local/bin added to your paths).

You can now also easily link other projects with pugixml with the -llibpugixml compiler flag.

2
votes

Did the pugi dev files come with a make file? If that's the case, you should be able to do a make, then a make install which should put the header files in the correct place.

If not, then I think the header files usually (on a linux box) go in to /usr/lib or /usr/lib64 for the .so files I think, and the .h files go in to /usr/include

Then you'll probably need up update your library paths by running ldconfig.

Once you've done that, you should add a -l to your makefile so for example if I wanted to write something using pthreads, I'd do:

gcc program1.cpp -lpthread -o outputFilename
2
votes

Pugixml is not really difficult to use, since it has no other dependencies that must be fulfilled. Simply compile your source and the pugi source and all works fine:

g++ -I./include/pugi -o [...] src/main.cpp include/pugi/pugixml.cpp

Your Makefile does not work for me, since the pugixml.cpp file is not found. Here a corrected version:

[...]
SRC = src
PUGIDIR = include/pugi
INCLUDES = ${PUGIDIR}
TARGET = main
[...]

$(TARGET): $(SRC)/$(TARGET).cpp ${PUGIDIR}/pugixml.cpp
    $(CC) $(CFLAGS) -I $(INCLUDES) -o $@ $^ 
[...]

In your main.cpp you have to include the pugixml.hpp simply by using

#include "pugixml.hpp"

See also C++ development on linux - where do I start? and How to make SIMPLE C++ Makefile? for some basics.

1
votes

I can't verify that right now, but most likely you just have to replace pugi/pugixml.cpp with include/pugi/pugixml.cpp.