Commit 19eefb3c2c33c54602a43ce12eeccfd5bf164e0f

Authored by Steven de Ridder
0 parents

Initial commit. dependencies not resolved yet.

.gitignore 0 → 100644
  1 +++ a/.gitignore
  1 +build/
  2 +CMakeLists.txt.user
... ...
CMakeLists.txt 0 → 100644
  1 +++ a/CMakeLists.txt
  1 +cmake_minimum_required(VERSION 3.0)
  2 +
  3 +# Check to see where cmake is located.
  4 +if( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake )
  5 + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
  6 +elseif( IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../cmake )
  7 + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
  8 +else()
  9 + return()
  10 +endif()
  11 +
  12 +# Check to see if there is versioning information available
  13 +if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake)
  14 + LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/osdev_versioning/cmake)
  15 + include(osdevversion)
  16 +endif()
  17 +
  18 +include(projectheader)
  19 +project_header(osdev_jobscheduler)
  20 +
  21 +add_subdirectory(src)
  22 +add_subdirectory(tests)
  23 +
  24 +# include(packaging)
  25 +# package_component()
... ...
README.md 0 → 100644
  1 +++ a/README.md
... ...
src/CMakeLists.txt 0 → 100644
  1 +++ a/src/CMakeLists.txt
  1 +cmake_minimum_required(VERSION 3.0)
  2 +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../../external/hsoa_create_version_include/cmake)
  3 +include(projectheader)
  4 +project_header(jobscheduler)
  5 +
  6 +find_package( Qt5Core REQUIRED )
  7 +find_package( Qt5Sql REQUIRED )
  8 +
  9 +include_directories( SYSTEM
  10 + ${Qt5Core_INCLUDE_DIRS}
  11 +)
  12 +
  13 +include(compiler)
  14 +
  15 +include_directories(
  16 + ${CMAKE_CURRENT_SOURCE_DIR}/../logutils
  17 + ${CMAKE_CURRENT_SOURCE_DIR}/../config
  18 + ${CMAKE_CURRENT_SOURCE_DIR}/../dcxml
  19 + ${CMAKE_CURRENT_SOURCE_DIR}/../global
  20 +)
  21 +
  22 +set(SRC_LIST
  23 + ${CMAKE_CURRENT_SOURCE_DIR}/jobscheduler.cpp
  24 + ${CMAKE_CURRENT_SOURCE_DIR}/jobtimer.cpp
  25 +)
  26 +
  27 +include(qtmoc)
  28 +create_mocs( SRC_LIST MOC_LIST
  29 + ${CMAKE_CURRENT_SOURCE_DIR}/jobscheduler.h
  30 + ${CMAKE_CURRENT_SOURCE_DIR}/jobtimer.h
  31 +)
  32 +
  33 +set_source_files_properties(
  34 + ${MOC_LIST}
  35 + PROPERTIES
  36 + COMPILE_FLAGS -Wno-undefined-reinterpret-cast
  37 +)
  38 +
  39 +link_directories(
  40 + ${CMAKE_BINARY_DIR}/lib
  41 +)
  42 +
  43 +include(library)
  44 +add_libraries(
  45 + ${Qt5Core_LIBRARIES}
  46 + logutils
  47 + global
  48 + config
  49 +)
  50 +
  51 +include(installation)
  52 +install_component()
... ...
src/jobscheduler.cpp 0 → 100644
  1 +++ a/src/jobscheduler.cpp
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +
  23 +#include "jobscheduler.h"
  24 +#include "log.h"
  25 +
  26 +using namespace osdev::components;
  27 +
  28 +JobScheduler::JobScheduler( QObject *_parent )
  29 + : QObject( _parent )
  30 + , m_jobSchedule()
  31 +{
  32 +
  33 +}
  34 +
  35 +JobScheduler::JobScheduler( const QList<JobData>& lstJobs, QObject *_parent )
  36 + : QObject( _parent )
  37 + , m_jobSchedule()
  38 +{
  39 + for( const auto& jobItem : lstJobs )
  40 + {
  41 + this->createJob( jobItem );
  42 + }
  43 +}
  44 +
  45 +void JobScheduler::slotRunJob( const QString& jobName )
  46 +{
  47 + LogInfo( "JobScheduler::slotRunJob", QString( "SlotRunJob fired for job : %1" ).arg( jobName ) );
  48 + if( m_jobSchedule.contains( jobName ) )
  49 + {
  50 + emit signalRunJob( m_jobSchedule.value( jobName )->jobData() );
  51 + }
  52 +
  53 + this->rescheduleJob( jobName );
  54 +}
  55 +
  56 +void JobScheduler::scheduleJob( const QString& _job_name,
  57 + const QHash<QString, QVariant>& _param_list,
  58 + const int _interval,
  59 + const QString& _target_object,
  60 + const QTime& _run_time,
  61 + const QDate& _run_date )
  62 +{
  63 +
  64 + // Get the corresponding job from the map.
  65 + QPointer<JobTimer> schedJob = m_jobSchedule.value( _job_name );
  66 + if( nullptr == schedJob )
  67 + {
  68 + LogInfo( "[JobScheduler::scheduleJob]", QString( "Job %1 not added to the an unknown error." ).arg( _job_name ) );
  69 + return;
  70 + }
  71 +
  72 + // Scheduling is done within 24 Hours. After each timer-fire we check if the rundate is equal to the currentDate.
  73 + // If so.. Run the job. If not, reschedule for the next 24 hours.
  74 + if( _run_time.isValid() || _run_date.isValid() )
  75 + {
  76 + QDateTime schedDT( QDate::currentDate(), _run_time );
  77 +
  78 + if( QDateTime::currentDateTime().secsTo( schedDT ) <= 0 )
  79 + {
  80 + // Add one day from today.
  81 + schedDT = schedDT.addDays( 1 );
  82 + }
  83 +
  84 + // Check if the jobName already exists. If so, re-schedule the existing job. If not, create the job.
  85 + if( !m_jobSchedule.contains( _job_name ) )
  86 + {
  87 + this->createJob( JobData( _job_name, _param_list, _run_time, _interval, _target_object, _run_date ) );
  88 + }
  89 +
  90 + if( 0 == _interval )
  91 + {
  92 + qint64 secondsToRun = QDateTime::currentDateTime().secsTo( schedDT );
  93 + schedJob->start( static_cast<int>( secondsToRun * 1000 ) );
  94 + }
  95 + }
  96 + else if( 0 < _interval )
  97 + {
  98 + schedJob->start( _interval * 1000 );
  99 + }
  100 +
  101 + if( schedJob->isActive() )
  102 + {
  103 + LogInfo( "[JobScheduler::scheduleJob]", QString( "Job : %1 scheduled to run in %2 seconds ( Date : %3, Time : %4 )" )
  104 + .arg( _job_name )
  105 + .arg( schedJob->remainingTime() / 1000 )
  106 + .arg( QDateTime::currentDateTime().addSecs( schedJob->remainingTime() / 1000 ).date().toString() )
  107 + .arg( QDateTime::currentDateTime().addSecs( schedJob->remainingTime() / 1000 ).time().toString() ) );
  108 + }
  109 + else
  110 + {
  111 + LogError( "[JobScheduler::scheduleJob]", QString( "Job : %1 failed to schedule." )
  112 + .arg( _job_name ) );
  113 +
  114 + }
  115 +}
  116 +
  117 +void JobScheduler::rescheduleJob ( const QString& _job_name )
  118 +{
  119 + if( m_jobSchedule.contains( _job_name ) )
  120 + {
  121 + QPointer<JobTimer> schedJob = m_jobSchedule.value( _job_name );
  122 + if( nullptr == schedJob )
  123 + {
  124 + LogInfo( "[JobScheduler::rescheduleJob]", QString( "Job %1 not rescheduled to the an unknown error." ).arg( _job_name ) );
  125 + return;
  126 + }
  127 +
  128 + this->scheduleJob( _job_name,
  129 + schedJob->jobData().paramList(),
  130 + schedJob->jobData().runInterval(),
  131 + schedJob->jobData().targetObject(),
  132 + schedJob->jobData().runTime(),
  133 + schedJob->jobData().runDate() );
  134 + }
  135 + else
  136 + {
  137 + LogError( "[JobScheduler::scheduleJob]", QString( "Unable to re-schedule job %1 as it is unknown." ).arg( _job_name ) );
  138 + }
  139 +}
  140 +
  141 +void JobScheduler::createJob( const JobData& _jobItem )
  142 +{
  143 + QPointer<JobTimer> newJob = new JobTimer( _jobItem, this ); // Create a new job
  144 + m_jobSchedule.insert( _jobItem.jobName(), newJob ); // Add to the list
  145 + QObject::connect( newJob.data(), &JobTimer::signalRunJob, this, &JobScheduler::slotRunJob );
  146 +}
  147 +
  148 +void JobScheduler::start()
  149 +{
  150 + // Schedule all stored jobs.
  151 + for( const QString& _jobName : QStringList( m_jobSchedule.keys() ) )
  152 + {
  153 + this->rescheduleJob( _jobName );
  154 + }
  155 +}
... ...
src/jobscheduler.h 0 → 100644
  1 +++ a/src/jobscheduler.h
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +
  23 +#ifndef OSDEV_COMPONENTS_JOBSCHEDULER_H
  24 +#define OSDEV_COMPONENTS_JOBSCHEDULER_H
  25 +
  26 +#include "jobdata.h"
  27 +#include "jobtimer.h"
  28 +
  29 +#include <QObject>
  30 +#include <QUuid>
  31 +#include <QHash>
  32 +#include <QString>
  33 +#include <QPointer>
  34 +
  35 +namespace osdev {
  36 +namespace components {
  37 +/*
  38 + * ________________________________________
  39 + * / Real computer scientists despise the \
  40 + * | idea of actual hardware. Hardware has |
  41 + * | limitations, software doesn't. It's a |
  42 + * | real shame that Turing machines are so |
  43 + * \ poor at I/O. /
  44 + * ----------------------------------------
  45 + * \
  46 + * \
  47 + * .--.
  48 + * |o_o |
  49 + * |:_/ |
  50 + * // \ \
  51 + * (| | )
  52 + * /'\_ _/`\
  53 + * \___)=(___/
  54 + *//*!
  55 + * \brief Schedule jobs to be executed on a specific Date / Time or on a regular interval.
  56 + * At this moment, it will schedule in intervals of 24 hours, if the date is set in the past.
  57 + * If the dat is in the future, it will be run for the first time on that specific date / Time
  58 + * and after that every 24 hours.
  59 + */
  60 +class JobScheduler : public QObject
  61 +{
  62 + Q_OBJECT
  63 +
  64 +public:
  65 + /*!
  66 + * \brief JobScheduler Constructor.
  67 + * \param _parent - The object creating the JobScheduler.
  68 + * By adding this pointer, we use the cascade deletion construction that comes with Qt.
  69 + */
  70 + JobScheduler( QObject *_parent = nullptr );
  71 +
  72 + /*!
  73 + * \brief JobScheduler Constructor
  74 + * \param lstJobs - The list of jobs, coming from the configuration that we want to have scheduled.
  75 + * \param _parent - The object creating the JobScheduler.
  76 + * By adding this pointer, we use the cascade deletion construction that comes with Qt.
  77 + */
  78 + JobScheduler( const QList < osdev::components::JobData > &lstJobs, QObject * _parent = nullptr );
  79 +
  80 + /*!
  81 + * \brief Schedule the job based on the parameters and settings.
  82 + * \param _job_name - The name of this job used for registration
  83 + * \param _param_list - Parameters by the job.
  84 + * \param _interval - The interval this job should be scheduled to.
  85 + * \param _target_object - The plugin or object this job is intended for. Used as input to the pluginmanager.
  86 + * \param _run_time - The time this job should run. Empty or '*' if interval based.
  87 + * \param _run_date - The date this job should run. Empty or '*' if daily.
  88 + */
  89 + void scheduleJob( const QString& _job_name,
  90 + const QHash<QString, QVariant>& _param_list,
  91 + const int _interval,
  92 + const QString& _target_object,
  93 + const QTime& _run_time,
  94 + const QDate& _run_date = QDate::currentDate() );
  95 +
  96 + /*!
  97 + * \brief Added for convenience. It will search for the given name and try to reschedule the job.
  98 + */
  99 + void rescheduleJob( const QString& _jobName );
  100 +
  101 + /*!
  102 + * \brief Start and initialize the jobscheduler.
  103 + * All configured Jobs are started and reported to the logfile.
  104 + */
  105 + void start();
  106 +
  107 +private:
  108 + /*!
  109 + * \brief Create the requested job and add it to the Schedule list.
  110 + * \param _jobItem - Structure containing the data needed for this job.
  111 + */
  112 + void createJob( const JobData& _jobItem );
  113 +
  114 + QHash<QString, QPointer<JobTimer>> m_jobSchedule; ///< The scheduler list
  115 +
  116 +private slots:
  117 + /*!
  118 + * \brief Called by the TimerObjects. It's identification (jobName) is used to determine
  119 + * which job has to be run
  120 + */
  121 + void slotRunJob( const QString& jobName );
  122 +
  123 +signals:
  124 + void signalRunJob( const JobData& data );
  125 +
  126 +};
  127 +
  128 +} /* End namespace components */
  129 +} /* End namespace osdev */
  130 +
  131 +#endif /* OSDEV_COMPONENTS_JOBSCHEDULER_H */
... ...
src/jobtimer.cpp 0 → 100644
  1 +++ a/src/jobtimer.cpp
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +
  23 +#include "jobtimer.h"
  24 +
  25 +using namespace osdev::components;
  26 +
  27 +JobTimer::JobTimer( const JobData& _jobData, QObject *_parent )
  28 + : QTimer( _parent )
  29 + , m_jobData( _jobData )
  30 +{
  31 + this->setTimerType( Qt::PreciseTimer ); ///< 1 millisecond accuracy
  32 + this->setSingleShot( true ); ///< Fire once. This timer has to be rescheduled.
  33 +
  34 + connectSignalsSlots();
  35 +}
  36 +
  37 +JobTimer::~JobTimer()
  38 +{
  39 +}
  40 +
  41 +void JobTimer::connectSignalsSlots()
  42 +{
  43 + QObject::connect( this, &QTimer::timeout, this, &JobTimer::slotTimeOut );
  44 +}
  45 +
  46 +void JobTimer::slotTimeOut()
  47 +{
  48 + emit JobTimer::signalRunJob( m_jobData.jobName() );
  49 +}
... ...
src/jobtimer.h 0 → 100644
  1 +++ a/src/jobtimer.h
  1 +/* ****************************************************************************
  2 + * Copyright 2019 Open Systems Development BV *
  3 + * *
  4 + * Permission is hereby granted, free of charge, to any person obtaining a *
  5 + * copy of this software and associated documentation files (the "Software"), *
  6 + * to deal in the Software without restriction, including without limitation *
  7 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, *
  8 + * and/or sell copies of the Software, and to permit persons to whom the *
  9 + * Software is furnished to do so, subject to the following conditions: *
  10 + * *
  11 + * The above copyright notice and this permission notice shall be included in *
  12 + * all copies or substantial portions of the Software. *
  13 + * *
  14 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
  15 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
  16 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
  17 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
  18 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *
  19 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *
  20 + * DEALINGS IN THE SOFTWARE. *
  21 + * ***************************************************************************/
  22 +
  23 +#ifndef OSDEV_COMPONENTS_JOBTIMER_H
  24 +#define OSDEV_COMPONENTS_JOBTIMER_H
  25 +
  26 +#include <QTimer>
  27 +#include <QObject>
  28 +#include <QDateTime>
  29 +#include <QHash>
  30 +#include <QString>
  31 +#include <QVariant>
  32 +#include <QSharedPointer>
  33 +
  34 +#include "jobdata.h"
  35 +
  36 +namespace osdev {
  37 +namespace components {
  38 +
  39 +/* ______________________________________
  40 + * / Got a dictionary? I want to know the \
  41 + * \ meaning of life. /
  42 + * --------------------------------------
  43 + * \
  44 + * \
  45 + * .--.
  46 + * |o_o |
  47 + * |:_/ |
  48 + * // \ \
  49 + * (| | )
  50 + * /'\_ _/`\
  51 + * \___)=(___/
  52 + *//*!
  53 + * \brief Instead of using separate timers, The job *is* the timer.
  54 + */
  55 +class JobTimer : public QTimer
  56 +{
  57 + Q_OBJECT
  58 +public:
  59 + /*!
  60 + * \brief C'tor taking a jobdata structure to initialize the jobTimer.
  61 + */
  62 + JobTimer( const JobData& _jobData, QObject *_parent = nullptr );
  63 +
  64 + /*!
  65 + * \brief D'tor
  66 + */
  67 + virtual ~JobTimer();
  68 +
  69 + /*!
  70 + * \brief Get the jobData as a structure from the scheduled object.
  71 + * \return The data as object. Empty if no data was set.
  72 + */
  73 + JobData jobData() const { return m_jobData; }
  74 +
  75 +private:
  76 + /*!
  77 + * \brief Connect all internal signals and slots.
  78 + */
  79 + void connectSignalsSlots();
  80 +
  81 + JobData m_jobData; ///< Structure to hold all jobInformation.
  82 +
  83 +signals:
  84 + /*!
  85 + * \brief Send the requested job by its name to all connected libraries.
  86 + */
  87 + void signalRunJob( const QString& jobName );
  88 +
  89 +private slots:
  90 + /*!
  91 + * \brief Will be called by the parent object (QTimer) times out.
  92 + */
  93 + void slotTimeOut();
  94 +
  95 +};
  96 +
  97 +} /* End namespace components */
  98 +} /* End namespace osdev */
  99 +
  100 +#endif /* OSDEV_COMPONENTS_JOBTIMER_H */
... ...
tests/CMakeLists.txt 0 → 100644
  1 +++ a/tests/CMakeLists.txt
  1 +cmake_minimum_required(VERSION 3.0)
  2 +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
  3 +
  4 +include(projectheader)
  5 +project_header(test_logutils)
  6 +
  7 +include_directories( SYSTEM
  8 + ${CMAKE_CURRENT_SOURCE_DIR}/../../src
  9 +)
  10 +
  11 +include(compiler)
  12 +set(SRC_LIST
  13 +)
  14 +
  15 +# add_executable( ${PROJECT_NAME}
  16 +# ${SRC_LIST}
  17 +# )
  18 +
  19 +# target_link_libraries(
  20 +# ${PROJECT_NAME}
  21 +# )
  22 +
  23 +# set_target_properties( ${PROJECT_NAME} PROPERTIES
  24 +# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
  25 +# LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
  26 +# ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/archive
  27 +# )
  28 +
  29 +# include(installation)
  30 +# install_application()
... ...