Creating HPX projects¶
Using HPX with pkg-config¶
How to build HPX applications with pkg-config¶
After you are done installing HPX, you should be able to build the following
program. It prints Hello World!
on the locality you run it on.
// Copyright (c) 2007-2012 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///////////////////////////////////////////////////////////////////////////////
// The purpose of this example is to execute a HPX-thread printing
// "Hello World!" once. That's all.
//[simplest_hello_world_1_getting_started
// Including 'hpx/hpx_main.hpp' instead of the usual 'hpx/hpx_init.hpp' enables
// to use the plain C-main below as the direct main HPX entry point.
#include <hpx/hpx_main.hpp>
#include <hpx/include/iostreams.hpp>
int main()
{
// Say hello to the world!
hpx::cout << "Hello World!\n" << hpx::flush;
return 0;
}
//]
Copy the text of this program into a file called hello_world.cpp.
Now, in the directory where you put hello_world.cpp, issue the following
commands (where $HPX_LOCATION
is the build directory or
CMAKE_INSTALL_PREFIX
you used while building HPX):
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HPX_LOCATION/lib/pkgconfig
c++ -o hello_world hello_world.cpp \
`pkg-config --cflags --libs hpx_application`\
-lhpx_iostreams -DHPX_APPLICATION_NAME=hello_world
Important
When using pkg-config with HPX, the pkg-config flags must go after the
-o
flag.
Note
HPX libraries have different names in debug and release mode. If you want
to link against a debug HPX library, you need to use the _debug
suffix
for the pkg-config name. That means instead of hpx_application
or
hpx_component
you will have to use hpx_application_debug
or
hpx_component_debug
Moreover, all referenced HPX components need to
have a appended d
suffix, e.g. instead of -lhpx_iostreams
you will
need to specify -lhpx_iostreamsd
.
Important
If the HPX libraries are in a path that is not found by the dynamic
linker. You need to add the path $HPX_LOCATION/lib
to your linker search
path (for example LD_LIBRARY_PATH
on Linux).
To test the program, type:
./hello_world
which should print Hello World!
and exit.
How to build HPX components with pkg-config¶
Let’s try a more complex example involving an HPX component. An HPX component is a class which exposes HPX actions. HPX components are compiled into dynamically loaded modules called component libraries. Here’s the source code:
hello_world_component.cpp
#include "hello_world_component.hpp"
#include <hpx/include/iostreams.hpp>
#include <iostream>
namespace examples { namespace server
{
void hello_world::invoke()
{
hpx::cout << "Hello HPX World!" << std::endl;
}
}}
HPX_REGISTER_COMPONENT_MODULE();
typedef hpx::components::component<
examples::server::hello_world
> hello_world_type;
HPX_REGISTER_COMPONENT(hello_world_type, hello_world);
HPX_REGISTER_ACTION(
examples::server::hello_world::invoke_action, hello_world_invoke_action);
hello_world_component.hpp
#if !defined(HELLO_WORLD_COMPONENT_HPP)
#define HELLO_WORLD_COMPONENT_HPP
#include <hpx/hpx.hpp>
#include <hpx/include/actions.hpp>
#include <hpx/include/lcos.hpp>
#include <hpx/include/components.hpp>
#include <hpx/include/serialization.hpp>
#include <utility>
namespace examples { namespace server
{
struct HPX_COMPONENT_EXPORT hello_world
: hpx::components::component_base<hello_world>
{
void invoke();
HPX_DEFINE_COMPONENT_ACTION(hello_world, invoke);
};
}}
HPX_REGISTER_ACTION_DECLARATION(
examples::server::hello_world::invoke_action, hello_world_invoke_action);
namespace examples
{
struct hello_world
: hpx::components::client_base<hello_world, server::hello_world>
{
typedef hpx::components::client_base<hello_world, server::hello_world>
base_type;
hello_world(hpx::future<hpx::naming::id_type> && f)
: base_type(std::move(f))
{}
hello_world(hpx::naming::id_type && f)
: base_type(std::move(f))
{}
void invoke()
{
hpx::async<server::hello_world::invoke_action>(this->get_id()).get();
}
};
}
#endif // HELLO_WORLD_COMPONENT_HPP
hello_world_client.cpp
// Copyright (c) 2012 Bryce Lelbach
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//[hello_world_client_getting_started
#include "hello_world_component.hpp"
#include <hpx/hpx_init.hpp>
int hpx_main(boost::program_options::variables_map&)
{
{
// Create a single instance of the component on this locality.
examples::hello_world client =
hpx::new_<examples::hello_world>(hpx::find_here());
// Invoke the component's action, which will print "Hello World!".
client.invoke();
}
return hpx::finalize(); // Initiate shutdown of the runtime system.
}
int main(int argc, char* argv[])
{
return hpx::init(argc, argv); // Initialize and run HPX.
}
//]
Copy the three source files above into three files (called
hello_world_component.cpp
, hello_world_component.hpp
and
hello_world_client.cpp
respectively).
Now, in the directory where you put the files, run the following command to
build the component library. (where $HPX_LOCATION
is the build directory or
CMAKE_INSTALL_PREFIX
you used while building HPX):
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HPX_LOCATION/lib/pkgconfig
c++ -o libhpx_hello_world.so hello_world_component.cpp \
`pkg-config --cflags --libs hpx_component` \
-lhpx_iostreams -DHPX_COMPONENT_NAME=hpx_hello_world
Now pick a directory in which to install your HPX component libraries. For
this example, we’ll choose a directory named my_hpx_libs
:
mkdir ~/my_hpx_libs
mv libhpx_hello_world.so ~/my_hpx_libs
Note
HPX libraries have different names in debug and release mode. If you want
to link against a debug HPX library, you need to use the _debug
suffix
for the pkg-config name. That means instead of hpx_application
or
hpx_component
you will have to use hpx_application_debug
or
hpx_component_debug
. Moreover, all referenced HPX components need to
have a appended d
suffix, e.g. instead of -lhpx_iostreams
you will
need to specify -lhpx_iostreamsd
.
Important
If the HPX libraries are in a path that is not found by the dynamic linker.
You need to add the path $HPX_LOCATION/lib
to your linker search path
(for example LD_LIBRARY_PATH
on Linux).
Now, to build the application that uses this component (hello_world_client.cpp
),
we do:
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$HPX_LOCATION/lib/pkgconfig
c++ -o hello_world_client hello_world_client.cpp \
``pkg-config --cflags --libs hpx_application``\
-L${HOME}/my_hpx_libs -lhpx_hello_world -lhpx_iostreams
Important
When using pkg-config with HPX, the pkg-config flags must go after the
-o
flag.
Finally, you’ll need to set your LD_LIBRARY_PATH before you can run the program. To run the program, type:
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$HOME/my_hpx_libs"
./hello_world_client
which should print Hello HPX World!
and exit.
Using HPX with CMake-based projects¶
In Addition to the pkg-config support discussed on the previous pages, HPX comes with full CMake support. In order to integrate HPX into your existing, or new CMakeLists.txt you can leverage the find_package command integrated into CMake. Following is a Hello World component example using CMake.
Let’s revisit what we have. We have three files which compose our example application:
hello_world_component.hpp
hello_world_component.cpp
hello_world_client.hpp
The basic structure to include HPX into your CMakeLists.txt is shown here:
# Require a recent version of cmake
cmake_minimum_required(VERSION 3.3.2 FATAL_ERROR)
# This project is C++ based.
project(your_app CXX)
# Instruct cmake to find the HPX settings
find_package(HPX)
In order to have CMake find HPX, it needs to be told where to look for the
HPXConfig.cmake
file that is generated when HPX is built or installed, it is
used by find_package(HPX)
to set up all the necessary macros needed to use
HPX in your project. The ways to achieve this are:
set the
HPX_DIR
cmake variable to point to the directory containing theHPXConfig.cmake
script on the command line when you invoke cmake:cmake -DHPX_DIR=$HPX_LOCATION/lib/cmake/HPX ...
where
$HPX_LOCATION
is the build directory orCMAKE_INSTALL_PREFIX
you used when building/configuring HPX.set the
CMAKE_PREFIX_PATH
variable to the root directory of your HPX build or install location on the command line when you invoke cmake:cmake -DCMAKE_PREFIX_PATH=$HPX_LOCATION ...
the difference between
CMAKE_PREFIX_PATH
andHPX_DIR
is that cmake will add common postfixes such aslib/cmake/<project
to theMAKE_PREFIX_PATH
and search in these locations too. Note that if your project uses HPX as well as other cmake managed projects, the paths to the locations of these multiple projects may be concatenated in theCMAKE_PREFIX_PATH
.The variables above may be set in the CMake GUI or curses ccmake interface instead of the command line.
Additionally, if you wish to require HPX for your project, replace the
find_package(HPX)
line with find_package(HPX REQUIRED)
.
You can check if HPX was successfully found with the HPX_FOUND
CMake variable.
The simplest way to add the HPX component is to use the add_hpx_component
macro and add it to the CMakeLists.txt
file:
# build your application using HPX
add_hpx_component(hello_world
SOURCES hello_world_component.cpp
HEADERS hello_world_component.hpp
COMPONENT_DEPENDENCIES iostreams)
Note
add_hpx_component
adds a _component
suffix to the target name. In the
example above a hello_world_component
target will be created.
The available options to add_hpx_component
are:
SOURCES
: The source files for that componentHEADERS
: The header files for that componentDEPENDENCIES
: Other libraries or targets this component depends onCOMPONENT_DEPENDENCIES
: The components this component depends onPLUGIN
: Treat this component as a plugin-able libraryCOMPILE_FLAGS
: Additional compiler flagsLINK_FLAGS
: Additional linker flagsFOLDER
: Add the headers and source files to this Source Group folder
EXCLUDE_FROM_ALL
: Do not build this component as part of theall
target
After adding the component, the way you add the executable is as follows:
# build your application using HPX
add_hpx_executable(hello_world
ESSENTIAL
SOURCES hello_world_client.cpp
COMPONENT_DEPENDENCIES hello_world)
Note
add_hpx_executable
adds a _exe
suffix to the target name. In the
example above a hello_world_exe
target will be created. In addition, a
_component
suffix will automatically be added to dependencies specified
in COMPONENT_DEPENDENCIES
, meaning you can directly use the name given
when adding a component using add_hpx_component
.
When you configure your application, all you need to do is set the HPX_DIR
variable to point to the installation of HPX!
Note
All library targets built with HPX are exported and readily available to be
used as arguments to target_link_libraries
in your targets. The HPX include directories are available with the
HPX_INCLUDE_DIRS
CMake variable.
CMake macros to integrate HPX into existing applications¶
In addition to the add_hpx_component
and add_hpx_executable
you can use
the hpx_setup_target
macro to have an already existing target to be used
with the HPX libraries:
hpx_setup_target(target)
Optional parameters are:
EXPORT
: Adds it to the CMake export list HPXTargetsINSTALL
: Generates a install rule for the targetPLUGIN
: Treat this component as a plugin-able libraryTYPE
: The type can be: EXECUTABLE, LIBRARY or COMPONENTDEPENDENCIES
: Other libraries or targets this component depends onCOMPONENT_DEPENDENCIES
: The components this component depends onCOMPILE_FLAGS
: Additional compiler flagsLINK_FLAGS
: Additional linker flags
If you do not use CMake, you can still build against HPX but you should refer to the section on How to build HPX components with pkg-config.
Note
Since HPX relies on dynamic libraries, the dynamic linker needs to know
where to look for them. If HPX isn’t installed into a path which is
configured as a linker search path, external projects need to either set
RPATH
or adapt LD_LIBRARY_PATH
to point to where the hpx libraries
reside. In order to set RPATH
s, you can include HPX_SetFullRPATH
in
your project after all libraries you want to link against have been added.
Please also consult the CMake documentation here.
Using HPX with Makefile¶
A basic project building with HPX is through creating makefiles. The process
of creating one can get complex depending upon the use of cmake parameter
HPX_WITH_HPX_MAIN
(which defaults to ON).
How to build HPX applications with makefile¶
If HPX is installed correctly, you should be able to build and run a simple
hello world program. It prints Hello World!
on the locality you
run it on.
// Copyright (c) 2007-2012 Hartmut Kaiser
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///////////////////////////////////////////////////////////////////////////////
// The purpose of this example is to execute a HPX-thread printing
// "Hello World!" once. That's all.
//[simplest_hello_world_1_getting_started
// Including 'hpx/hpx_main.hpp' instead of the usual 'hpx/hpx_init.hpp' enables
// to use the plain C-main below as the direct main HPX entry point.
#include <hpx/hpx_main.hpp>
#include <hpx/include/iostreams.hpp>
int main()
{
// Say hello to the world!
hpx::cout << "Hello World!\n" << hpx::flush;
return 0;
}
//]
Copy the content of this program into a file called hello_world.cpp.
Now in the directory where you put hello_world.cpp, create a Makefile. Add the following code:
CXX=(CXX) # Add your favourite compiler here or let makefile choose default.
CXXFLAGS=-O3 -std=c++17
BOOST_ROOT=/path/to/boost
HWLOC_ROOT=/path/to/hwloc
TCMALLOC_ROOT=/path/to/tcmalloc
HPX_ROOT=/path/to/hpx
INCLUDE_DIRECTIVES=$(HPX_ROOT)/include $(BOOST_ROOT)/include $(HWLOC_ROOT)/include
LIBRARY_DIRECTIVES=-L$(HPX_ROOT)/lib $(HPX_ROOT)/lib/libhpx_init.a $(HPX_ROOT)/lib/libhpx.so $(BOOST_ROOT)/lib/libboost_atomic-mt.so $(BOOST_ROOT)/lib/libboost_filesystem-mt.so $(BOOST_ROOT)/lib/libboost_program_options-mt.so $(BOOST_ROOT)/lib/libboost_regex-mt.so $(BOOST_ROOT)/lib/libboost_system-mt.so -lpthread $(TCMALLOC_ROOT)/libtcmalloc_minimal.so $(HWLOC_ROOT)/libhwloc.so -ldl -lrt
LINK_FLAGS=$(HPX_ROOT)/lib/libhpx_wrap.a -Wl,-wrap=main # should be left empty for HPX_WITH_HPX_MAIN=OFF
hello_world: hello_world.o
$(CXX) $(CXXFLAGS) -o hello_world hello_world.o $(LIBRARY_DIRECTIVES) $(LINK_FLAGS)
hello_world.o:
$(CXX) $(CXXFLAGS) -c -o hello_world.o hello_world.cpp $(INCLUDE_DIRECTIVES)
Important
LINK_FLAGS
should be left empty if HPX_WITH_HPX_MAIN is set to OFF.
Boost in the above example is build with --layout=tagged
. Actual boost
flags may vary on your build of boost.
To build the program, type:
make
A successfull build should result in hello_world binary. To test, type:
./hello_world
How to build HPX components with makefile¶
Let’s try a more complex example involving an HPX component. An HPX component is a class which exposes HPX actions. HPX components are compiled into dynamically loaded modules called component libraries. Here’s the source code:
hello_world_component.cpp
#include "hello_world_component.hpp"
#include <hpx/include/iostreams.hpp>
#include <iostream>
namespace examples { namespace server
{
void hello_world::invoke()
{
hpx::cout << "Hello HPX World!" << std::endl;
}
}}
HPX_REGISTER_COMPONENT_MODULE();
typedef hpx::components::component<
examples::server::hello_world
> hello_world_type;
HPX_REGISTER_COMPONENT(hello_world_type, hello_world);
HPX_REGISTER_ACTION(
examples::server::hello_world::invoke_action, hello_world_invoke_action);
hello_world_component.hpp
#if !defined(HELLO_WORLD_COMPONENT_HPP)
#define HELLO_WORLD_COMPONENT_HPP
#include <hpx/hpx.hpp>
#include <hpx/include/actions.hpp>
#include <hpx/include/lcos.hpp>
#include <hpx/include/components.hpp>
#include <hpx/include/serialization.hpp>
#include <utility>
namespace examples { namespace server
{
struct HPX_COMPONENT_EXPORT hello_world
: hpx::components::component_base<hello_world>
{
void invoke();
HPX_DEFINE_COMPONENT_ACTION(hello_world, invoke);
};
}}
HPX_REGISTER_ACTION_DECLARATION(
examples::server::hello_world::invoke_action, hello_world_invoke_action);
namespace examples
{
struct hello_world
: hpx::components::client_base<hello_world, server::hello_world>
{
typedef hpx::components::client_base<hello_world, server::hello_world>
base_type;
hello_world(hpx::future<hpx::naming::id_type> && f)
: base_type(std::move(f))
{}
hello_world(hpx::naming::id_type && f)
: base_type(std::move(f))
{}
void invoke()
{
hpx::async<server::hello_world::invoke_action>(this->get_id()).get();
}
};
}
#endif // HELLO_WORLD_COMPONENT_HPP
hello_world_client.cpp
// Copyright (c) 2012 Bryce Lelbach
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//[hello_world_client_getting_started
#include "hello_world_component.hpp"
#include <hpx/hpx_init.hpp>
int hpx_main(boost::program_options::variables_map&)
{
{
// Create a single instance of the component on this locality.
examples::hello_world client =
hpx::new_<examples::hello_world>(hpx::find_here());
// Invoke the component's action, which will print "Hello World!".
client.invoke();
}
return hpx::finalize(); // Initiate shutdown of the runtime system.
}
int main(int argc, char* argv[])
{
return hpx::init(argc, argv); // Initialize and run HPX.
}
//]
Now in the directory, create a Makefile. Add the following code:
CXX=(CXX) # Add your favourite compiler here or let makefile choose default.
CXXFLAGS=-O3 -std=c++17
BOOST_ROOT=/path/to/boost
HWLOC_ROOT=/path/to/hwloc
TCMALLOC_ROOT=/path/to/tcmalloc
HPX_ROOT=/path/to/hpx
INCLUDE_DIRECTIVES=$(HPX_ROOT)/include $(BOOST_ROOT)/include $(HWLOC_ROOT)/include
LIBRARY_DIRECTIVES=-L$(HPX_ROOT)/lib $(HPX_ROOT)/lib/libhpx_init.a $(HPX_ROOT)/lib/libhpx.so $(BOOST_ROOT)/lib/libboost_atomic-mt.so $(BOOST_ROOT)/lib/libboost_filesystem-mt.so $(BOOST_ROOT)/lib/libboost_program_options-mt.so $(BOOST_ROOT)/lib/libboost_regex-mt.so $(BOOST_ROOT)/lib/libboost_system-mt.so -lpthread $(TCMALLOC_ROOT)/libtcmalloc_minimal.so $(HWLOC_ROOT)/libhwloc.so -ldl -lrt
LINK_FLAGS=$(HPX_ROOT)/lib/libhpx_wrap.a -Wl,-wrap=main # should be left empty for HPX_WITH_HPX_MAIN=OFF
hello_world_client: libhpx_hello_world hello_world_client.o
$(CXX) $(CXXFLAGS) -o hello_world_client $(LIBRARY_DIRECTIVES) libhpx_hello_world $(LINK_FLAGS)
hello_world_client.o: hello_world_client.cpp
$(CXX) $(CXXFLAGS) -o hello_world_client.o hello_world_client.cpp $(INCLUDE_DIRECTIVES)
libhpx_hello_world: hello_world_component.o
$(CXX) $(CXXFLAGS) -o libhpx_hello_world hello_world_component.o $(LIBRARY_DIRECTIVES)
hello_world_component.o: hello_world_component.cpp
$(CXX) $(CXXFLAGS) -c -o hello_world_component.o hello_world_component.cpp $(INCLUDE_DIRECTIVES)
To build the program, type:
make
A successfull build should result in hello_world binary. To test, type:
./hello_world
Note
Due to high variations in CMake flags and library dependencies, it is recommended to build HPX applications and components with pkg-config or CMakeLists.txt. Writing Makefile may result in broken builds if due care is not taken. pkg-config files and CMake systems are configured with CMake build of HPX. Hence, they are stable and provides with better support overall.