allow config file interpolation

This commit is contained in:
Radovan Bast 2015-10-04 23:08:31 +02:00
parent 5890d462fe
commit a823e300f6
14 changed files with 71 additions and 80 deletions

View File

@ -1,32 +1,18 @@
#.rst: #.rst:
# #
# Boost libraries detection and automatic build-up. # Detect, build, and link Boost libraries.
# Minimum required version of Boost and required components have to # This modules downloads the .zip archive from SourceForge
# be specified separately in ``custom/boost_version-components.cmake`` # Autocmake update time.
# By "required components" we here mean compiled Boost libraries. #
# This modules downloads the .zip archive from sourceforge at project
# bootstrap.
# Your autocmake.cfg should look like this:: # Your autocmake.cfg should look like this::
# #
# [custom]
# source: custom/boost_version-components.cmake
#
# [boost] # [boost]
# {'major': 1, 'minor': 59, 'patch': 0, 'components': 'chrono;timer;system'}
# source: https://github.com/scisoft/autocmake/raw/master/modules/boost/boost.cmake # source: https://github.com/scisoft/autocmake/raw/master/modules/boost/boost.cmake
# fetch: http://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.zip
# #
# The ``custom/boost_version-components.cmake`` should look like this:: # Cross-dependencies between required components are not checked for.
# # For example, Boost.Timer depends on Boost.Chrono and Boost.System thus you
# set(BOOST_MINIMUM_REQUIRED 1.58.0) # should ask explicitly for all three.
# list(APPEND BOOST_COMPONENTS_REQUIRED chrono timer system)
#
# Caveats:
#
# #. cross-dependencies between required components are not checked for.
# For example, Boost.Timer depends on Boost.Chrono and Boost.System thus you
# should ask explicitly for all three
# #. the project admin has to make sure that ``BOOST_MINIMUM_REQUIRED`` and the
# ``fetch`` directive point to the same version of Boost
# #
# Dependencies:: # Dependencies::
# #
@ -35,17 +21,19 @@
# #
# Variables used:: # Variables used::
# #
# BOOST_MINIMUM_REQUIRED - Minimum required version of Boost, set in ``custom/boost_version-components.cmake`` # BOOST_MINIMUM_REQUIRED - Minimum required version of Boost
# BOOST_COMPONENTS_REQUIRED - Components (compiled Boost libraries) required # BOOST_COMPONENTS_REQUIRED - Components (compiled Boost libraries) required
# PROJECT_SOURCE_DIR # PROJECT_SOURCE_DIR
# PROJECT_BINARY_DIR # PROJECT_BINARY_DIR
# CMAKE_BUILD_TYPE # CMAKE_BUILD_TYPE
# MPI_FOUND # MPI_FOUND
# #
# Variables set::
#
# autocmake.cfg configuration:: # autocmake.cfg configuration::
# #
# major=1
# minor=48
# patch=0
# components=''
# fetch: https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_unpack.cmake # fetch: https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_unpack.cmake
# https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_userconfig.cmake # https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_userconfig.cmake
# https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_configure.cmake # https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_configure.cmake
@ -53,17 +41,20 @@
# https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_install.cmake # https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_install.cmake
# https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_headers.cmake # https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_headers.cmake
# https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_cleanup.cmake # https://github.com/scisoft/autocmake/raw/master/modules/boost/boost_cleanup.cmake
# http://sourceforge.net/projects/boost/files/boost/%(major)s.%(minor)s.%(patch)s/boost_%(major)s_%(minor)s_%(patch)s.zip
# docopt: --boost-headers=<BOOST_INCLUDEDIR> Include directories for Boost [default: '']. # docopt: --boost-headers=<BOOST_INCLUDEDIR> Include directories for Boost [default: ''].
# --boost-libraries=<BOOST_LIBRARYDIR> Library directories for Boost [default: '']. # --boost-libraries=<BOOST_LIBRARYDIR> Library directories for Boost [default: ''].
# --build-boost=<FORCE_CUSTOM_BOOST> Deactivate Boost detection and build on-the-fly <ON/OFF> [default: OFF]. # --build-boost=<FORCE_CUSTOM_BOOST> Deactivate Boost detection and build on-the-fly <ON/OFF> [default: OFF].
# define: '-DBOOST_INCLUDEDIR="%s"' % arguments['--boost-headers'] # define: '-DBOOST_INCLUDEDIR="{0}"'.format(arguments['--boost-headers'])
# '-DBOOST_LIBRARYDIR="%s"' % arguments['--boost-libraries'] # '-DBOOST_LIBRARYDIR="{0}"'.format(arguments['--boost-libraries'])
# '-DFORCE_CUSTOM_BOOST="%s"' % arguments['--build-boost'] # '-DFORCE_CUSTOM_BOOST="{0}"'.format(arguments['--build-boost'])
# '-DBOOST_MINIMUM_REQUIRED="%(major)s.%(minor)s.%(patch)s"'
# '-DBOOST_COMPONENTS_REQUIRED="%(components)s"'
# FIXME Maintainer should be able to choose between fail (end-user has to satisfy dependency # FIXME Maintainer should be able to choose between fail (end-user has to satisfy dependency
# on its own) and soft-fail (self-build of Boost) # on its own) and soft-fail (self-build of Boost)
# Underscore-separated version number # Underscore-separated version number
string(REGEX REPLACE "\\." "_" BOOSTVER ${BOOST_MINIMUM_REQUIRED}) string(REGEX REPLACE "\\." "_" BOOSTVER ${BOOST_MINIMUM_REQUIRED})
# Where the Boost .zip archive is located # Where the Boost .zip archive is located
set(BOOST_ARCHIVE_LOCATION ${CMAKE_CURRENT_LIST_DIR}) set(BOOST_ARCHIVE_LOCATION ${CMAKE_CURRENT_LIST_DIR})
set(BOOST_ARCHIVE boost_${BOOSTVER}.zip) set(BOOST_ARCHIVE boost_${BOOSTVER}.zip)
@ -82,12 +73,12 @@ if(FORCE_CUSTOM_BOOST)
# Just to avoid unused variable warning from CMake # Just to avoid unused variable warning from CMake
set(BOOST_INCLUDEDIR "") set(BOOST_INCLUDEDIR "")
set(BOOST_LIBRARYDIR "") set(BOOST_LIBRARYDIR "")
else(FORCE_CUSTOM_BOOST) else()
find_package(Boost ${BOOST_MINIMUM_REQUIRED} COMPONENTS "${BOOST_COMPONENTS_REQUIRED}") find_package(Boost ${BOOST_MINIMUM_REQUIRED} COMPONENTS "${BOOST_COMPONENTS_REQUIRED}")
if(NOT Boost_FOUND) if(NOT Boost_FOUND)
set(BUILD_CUSTOM_BOOST TRUE) set(BUILD_CUSTOM_BOOST TRUE)
endif(NOT Boost_FOUND) endif()
endif(FORCE_CUSTOM_BOOST) endif()
if(BUILD_CUSTOM_BOOST) if(BUILD_CUSTOM_BOOST)
## Preliminary work ## Preliminary work
@ -112,7 +103,8 @@ if(BUILD_CUSTOM_BOOST)
string(TOLOWER ${CMAKE_BUILD_TYPE} type) string(TOLOWER ${CMAKE_BUILD_TYPE} type)
include(${CMAKE_CURRENT_LIST_DIR}/boost_unpack.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_unpack.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/boost_userconfig.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_userconfig.cmake)
if(BOOST_COMPONENTS_REQUIRED)
if(NOT BOOST_COMPONENTS_REQUIRED STREQUAL "")
# Non-empty list. Compiled libraries needed # Non-empty list. Compiled libraries needed
# Transform the ;-separated list to a ,-separated list (digested by the Boost build toolchain!) # Transform the ;-separated list to a ,-separated list (digested by the Boost build toolchain!)
string(REPLACE ";" "," b2_needed_components "${BOOST_COMPONENTS_REQUIRED}") string(REPLACE ";" "," b2_needed_components "${BOOST_COMPONENTS_REQUIRED}")
@ -125,12 +117,13 @@ if(BUILD_CUSTOM_BOOST)
include(${CMAKE_CURRENT_LIST_DIR}/boost_configure.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_configure.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/boost_build.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_build.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/boost_install.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_install.cmake)
else(BOOST_COMPONENTS_REQUIRED) else()
# Empty list. Header-only libraries needed # Empty list. Header-only libraries needed
# Just unpack to known location # Just unpack to known location
message(STATUS " No libraries required, installing headers") message(STATUS " No libraries required, installing headers")
include(${CMAKE_CURRENT_LIST_DIR}/boost_headers.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_headers.cmake)
endif(BOOST_COMPONENTS_REQUIRED) endif()
include(${CMAKE_CURRENT_LIST_DIR}/boost_cleanup.cmake) include(${CMAKE_CURRENT_LIST_DIR}/boost_cleanup.cmake)
add_custom_target(custom_boost DEPENDS ${CUSTOM_BOOST_LOCATION}/boost.cleanedup) add_custom_target(custom_boost DEPENDS ${CUSTOM_BOOST_LOCATION}/boost.cleanedup)
# 4. Set all variables related to Boost that find_package would have set # 4. Set all variables related to Boost that find_package would have set
@ -142,6 +135,7 @@ if(BUILD_CUSTOM_BOOST)
set(Boost_LIB_VERSION ${Boost_MAJOR_VERSION}_${Boost_MINOR_VERSION}) set(Boost_LIB_VERSION ${Boost_MAJOR_VERSION}_${Boost_MINOR_VERSION})
set(Boost_INCLUDE_DIR ${CUSTOM_BOOST_LOCATION}/include CACHE PATH "Boost include directory" FORCE) set(Boost_INCLUDE_DIR ${CUSTOM_BOOST_LOCATION}/include CACHE PATH "Boost include directory" FORCE)
set(Boost_LIBRARY_DIR ${CUSTOM_BOOST_LOCATION}/lib CACHE PATH "Boost library directory" FORCE) set(Boost_LIBRARY_DIR ${CUSTOM_BOOST_LOCATION}/lib CACHE PATH "Boost library directory" FORCE)
foreach(_component ${BOOST_COMPONENTS_REQUIRED}) foreach(_component ${BOOST_COMPONENTS_REQUIRED})
string(TOUPPER ${_component} _COMP) string(TOUPPER ${_component} _COMP)
set(Boost_${_COMP}_FOUND TRUE) set(Boost_${_COMP}_FOUND TRUE)
@ -150,11 +144,14 @@ if(BUILD_CUSTOM_BOOST)
set(Boost_${_COMP}_LIBRARY_RELEASE ${Boost_LIBRARY_DIR}/${Boost_${_COMP}_LIBRARY} CACHE FILEPATH "Boost ${_component} library (release)" FORCE) set(Boost_${_COMP}_LIBRARY_RELEASE ${Boost_LIBRARY_DIR}/${Boost_${_COMP}_LIBRARY} CACHE FILEPATH "Boost ${_component} library (release)" FORCE)
list(APPEND Boost_LIBRARIES ${Boost_${_COMP}_LIBRARY}) list(APPEND Boost_LIBRARIES ${Boost_${_COMP}_LIBRARY})
endforeach() endforeach()
set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR})
set(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR}) set(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR})
if(CMAKE_SYSTEM_NAME MATCHES "Linux") if(CMAKE_SYSTEM_NAME MATCHES "Linux")
list(APPEND Boost_LIBRARIES rt) list(APPEND Boost_LIBRARIES rt)
endif() endif()
endif(BUILD_CUSTOM_BOOST) endif()
include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS}) link_directories(${Boost_LIBRARY_DIRS})

View File

@ -5,12 +5,9 @@ min_cmake_version: 2.8
[cxx] [cxx]
source: ../../../modules/cxx.cmake source: ../../../modules/cxx.cmake
[custom]
source: custom/boost_version-components.cmake
[boost] [boost]
defaults: {'major': 1, 'minor': 48, 'patch': 0}
source: ../../../modules/boost/boost.cmake source: ../../../modules/boost/boost.cmake
fetch: http://sourceforge.net/projects/boost/files/boost/1.48.0/boost_1_48_0.zip
[default_build_paths] [default_build_paths]
source: ../../../modules/default_build_paths.cmake source: ../../../modules/default_build_paths.cmake

View File

@ -1,2 +0,0 @@
set(BOOST_MINIMUM_REQUIRED 1.48.0)
list(APPEND BOOST_COMPONENTS_REQUIRED)

View File

@ -5,12 +5,9 @@ min_cmake_version: 2.8
[cxx] [cxx]
source: ../../../modules/cxx.cmake source: ../../../modules/cxx.cmake
[custom]
source: custom/boost_version-components.cmake
[boost] [boost]
defaults: {'major': 1, 'minor': 59, 'patch': 0, 'components': 'chrono;timer;system'}
source: ../../../modules/boost/boost.cmake source: ../../../modules/boost/boost.cmake
fetch: http://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.zip
[default_build_paths] [default_build_paths]
source: ../../../modules/default_build_paths.cmake source: ../../../modules/default_build_paths.cmake

View File

@ -1,2 +0,0 @@
set(BOOST_MINIMUM_REQUIRED 1.59.0)
list(APPEND BOOST_COMPONENTS_REQUIRED chrono timer system)

View File

@ -1,5 +1,13 @@
add_executable(example example.cpp) add_executable(example example.cpp)
if(BUILD_CUSTOM_BOOST) if(BUILD_CUSTOM_BOOST)
add_dependencies(example custom_boost) add_dependencies(example custom_boost)
endif() endif()
target_link_libraries(example ${Boost_TIMER_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY})
set(_libs ${Boost_TIMER_LIBRARY} ${Boost_CHRONO_LIBRARY} ${Boost_SYSTEM_LIBRARY})
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(_libs ${_libs} rt)
endif()
target_link_libraries(example ${_libs})

View File

@ -8,12 +8,9 @@ source: ../../../modules/cxx.cmake
[mpi] [mpi]
source: ../../../modules/mpi.cmake source: ../../../modules/mpi.cmake
[custom]
source: custom/boost_version-components.cmake
[boost] [boost]
defaults: {'major': 1, 'minor': 59, 'patch': 0, 'components': 'mpi;serialization'}
source: ../../../modules/boost/boost.cmake source: ../../../modules/boost/boost.cmake
fetch: http://sourceforge.net/projects/boost/files/boost/1.59.0/boost_1_59_0.zip
[default_build_paths] [default_build_paths]
source: ../../../modules/default_build_paths.cmake source: ../../../modules/default_build_paths.cmake

View File

@ -1,2 +0,0 @@
set(BOOST_MINIMUM_REQUIRED 1.59.0)
list(APPEND BOOST_COMPONENTS_REQUIRED mpi serialization)

View File

@ -1,5 +1,7 @@
add_executable(example example.cpp) add_executable(example example.cpp)
if(BUILD_CUSTOM_BOOST) if(BUILD_CUSTOM_BOOST)
add_dependencies(example custom_boost) add_dependencies(example custom_boost)
endif() endif()
target_link_libraries(example ${Boost_MPI_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} ${MPI_CXX_LIBRARIES}) target_link_libraries(example ${Boost_MPI_LIBRARY} ${Boost_SERIALIZATION_LIBRARY} ${MPI_CXX_LIBRARIES})

View File

@ -11,12 +11,9 @@ source: ../../../modules/python_interpreter.cmake
[python_libs] [python_libs]
source: ../../../modules/python_libs.cmake source: ../../../modules/python_libs.cmake
[custom]
source: custom/boost_version-components.cmake
[boost] [boost]
defaults: {'major': 1, 'minor': 56, 'patch': 0, 'components': 'python'}
source: ../../../modules/boost/boost.cmake source: ../../../modules/boost/boost.cmake
fetch: http://sourceforge.net/projects/boost/files/boost/1.56.0/boost_1_56_0.zip
[default_build_paths] [default_build_paths]
source: ../../../modules/default_build_paths.cmake source: ../../../modules/default_build_paths.cmake

View File

@ -1,2 +0,0 @@
set(BOOST_MINIMUM_REQUIRED 1.56.0)
list(APPEND BOOST_COMPONENTS_REQUIRED python)

View File

@ -1,5 +1,7 @@
add_executable(example example.cpp) add_executable(example example.cpp)
if(BUILD_CUSTOM_BOOST) if(BUILD_CUSTOM_BOOST)
add_dependencies(example custom_boost) add_dependencies(example custom_boost)
endif() endif()
target_link_libraries(example ${Boost_PYTHON_LIBRARY} ${PYTHON_LIBRARIES}) target_link_libraries(example ${Boost_PYTHON_LIBRARY} ${PYTHON_LIBRARIES})

View File

@ -170,21 +170,17 @@ def test_python_libs_custom():
configure_build_and_exe('python_libs_custom', 'python setup.py --cxx=g++ --python={}'.format(python_executable)) configure_build_and_exe('python_libs_custom', 'python setup.py --cxx=g++ --python={}'.format(python_executable))
@skip_always
def test_boost_header_only(): def test_boost_header_only():
configure_build_and_exe('boost_header_only', 'python setup.py --cxx=g++') configure_build_and_exe('boost_header_only', 'python setup.py --cxx=g++')
@skip_always
def test_boost_libs(): def test_boost_libs():
configure_build_and_exe('boost_libs', 'python setup.py --cxx=g++') configure_build_and_exe('boost_libs', 'python setup.py --cxx=g++')
@skip_always
def test_boost_mpi_libs(): def test_boost_mpi_libs():
configure_build_and_exe('boost_mpi_libs', 'python setup.py --cxx=g++ --mpi') configure_build_and_exe('boost_mpi_libs', 'python setup.py --cxx=g++ --mpi')
@skip_always
def test_boost_python_libs(): def test_boost_python_libs():
configure_build_and_exe('boost_python_libs', 'python setup.py --cxx=g++') configure_build_and_exe('boost_python_libs', 'python setup.py --cxx=g++')

View File

@ -3,13 +3,14 @@
import os import os
import sys import sys
import datetime import datetime
import ast
from collections import OrderedDict, namedtuple from collections import OrderedDict, namedtuple
# we do not use the nicer sys.version_info.major # we do not use the nicer sys.version_info.major
# for compatibility with Python < 2.7 # for compatibility with Python < 2.7
if sys.version_info[0] > 2: if sys.version_info[0] > 2:
from io import StringIO from io import StringIO
from configparser import RawConfigParser from configparser import ConfigParser
import urllib.request import urllib.request
class URLopener(urllib.request.FancyURLopener): class URLopener(urllib.request.FancyURLopener):
@ -18,7 +19,7 @@ if sys.version_info[0] > 2:
sys.exit(-1) sys.exit(-1)
else: else:
from StringIO import StringIO from StringIO import StringIO
from ConfigParser import RawConfigParser from ConfigParser import ConfigParser
import urllib import urllib
class URLopener(urllib.FancyURLopener): class URLopener(urllib.FancyURLopener):
@ -231,7 +232,7 @@ def gen_cmakelists(project_name, min_cmake_version, relative_path, modules):
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def prepend_or_set(config, section, option, value): def prepend_or_set(config, section, option, value, defaults):
""" """
If option is already set, then value is prepended. If option is already set, then value is prepended.
If option is not set, then it is created and set to value. If option is not set, then it is created and set to value.
@ -239,7 +240,7 @@ def prepend_or_set(config, section, option, value):
""" """
if value: if value:
if config.has_option(section, option): if config.has_option(section, option):
value += '\n%s' % config.get(section, option) value += '\n%s' % config.get(section, option, 0, defaults)
config.set(section, option, value) config.set(section, option, value)
return config return config
@ -287,12 +288,17 @@ def fetch_modules(config, relative_path):
sys.stderr.write("ERROR: %s does not exist\n" % src) sys.stderr.write("ERROR: %s does not exist\n" % src)
sys.exit(-1) sys.exit(-1)
if config.has_option(section, 'defaults'):
defaults = ast.literal_eval(config.get(section, 'defaults'))
else:
defaults = {}
# we infer config from the module documentation # we infer config from the module documentation
with open(file_name, 'r') as f: with open(file_name, 'r') as f:
config_docopt, config_define, config_export, config_fetch = parse_cmake_module(f.read()) config_docopt, config_define, config_export, config_fetch = parse_cmake_module(f.read(), defaults)
config = prepend_or_set(config, section, 'docopt', config_docopt) config = prepend_or_set(config, section, 'docopt', config_docopt, defaults)
config = prepend_or_set(config, section, 'define', config_define) config = prepend_or_set(config, section, 'define', config_define, defaults)
config = prepend_or_set(config, section, 'export', config_export) config = prepend_or_set(config, section, 'export', config_export, defaults)
if config_fetch: if config_fetch:
for src in config_fetch.split('\n'): for src in config_fetch.split('\n'):
dst = os.path.join(fetch_dst_directory, os.path.basename(src)) dst = os.path.join(fetch_dst_directory, os.path.basename(src))
@ -377,7 +383,7 @@ def main(argv):
# read config file # read config file
print('- parsing autocmake.cfg') print('- parsing autocmake.cfg')
config = RawConfigParser(dict_type=OrderedDict) config = ConfigParser(dict_type=OrderedDict)
config.read('autocmake.cfg') config.read('autocmake.cfg')
if not config.has_option('project', 'name'): if not config.has_option('project', 'name'):
@ -425,7 +431,7 @@ def make_executable(path):
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
def parse_cmake_module(s_in): def parse_cmake_module(s_in, defaults={}):
config_docopt = None config_docopt = None
config_define = None config_define = None
@ -456,18 +462,18 @@ def parse_cmake_module(s_in):
autocmake_entry = '[foo]\n' + autocmake_entry autocmake_entry = '[foo]\n' + autocmake_entry
buf = StringIO(autocmake_entry) buf = StringIO(autocmake_entry)
config = RawConfigParser(dict_type=OrderedDict) config = ConfigParser(dict_type=OrderedDict)
config.readfp(buf) config.readfp(buf)
for section in config.sections(): for section in config.sections():
if config.has_option(section, 'docopt'): if config.has_option(section, 'docopt'):
config_docopt = config.get(section, 'docopt') config_docopt = config.get(section, 'docopt', 0, defaults)
if config.has_option(section, 'define'): if config.has_option(section, 'define'):
config_define = config.get(section, 'define') config_define = config.get(section, 'define', 0, defaults)
if config.has_option(section, 'export'): if config.has_option(section, 'export'):
config_export = config.get(section, 'export') config_export = config.get(section, 'export', 0, defaults)
if config.has_option(section, 'fetch'): if config.has_option(section, 'fetch'):
config_fetch = config.get(section, 'fetch') config_fetch = config.get(section, 'fetch', 0, defaults)
return config_docopt, config_define, config_export, config_fetch return config_docopt, config_define, config_export, config_fetch