#!/usr/bin/env python3

"""bgun.py

Usage:
  bgun.py build [install (test [(--cmd <pattern>)]) ] [(-j <jobs>)] 
  bgun.py install 
  bgun.py test [(--cmd <pattern>)]
  bgun.py preprocess --template=<template> (--inputs=<json>... | --inputDir=<inputDir>) (--output=<outputFile> | --splitByName=<pattern>) [--dryRun --verbose --settings=<json settings>]  
  bgun.py [gen] [--staticinstall --debug --force --cmakeflags=<cmakeOptions> --cxxflags=<cxxflags> --cflags=<cxxflags> --lflags=<lflags> --noSubfolders] <path>  [--verbose] 
  bgun.py --version  
  bgun.py show (--list | --all | --refresh | --changed  <starting_sha> | ([--add | --remove] <components>...) ) [--parent] [--hard] 
  bgun.py bootstrap gtest --name=<name> <component> 
  bgun.py bootstrap product --name=<name> 
  bgun.py bootstrap module --name=<name> 
  bgun.py bootstrap template --name=<name> <component> 
  bgun.py diff <fileA> <fileB>
  bgun.py res <fileName> [(-o <outputFile>)]
  bgun.py ver --out=<outputFile> [--git  --suffix=<suffix> --input=<fileName> --installationSuffix=<installationSuffix>]

Example:   
  bgun.py --linkFromDist=lib/mylib1,lib/mylib2
  bgun.py show product/myproduct1 product/myproduct2 --hard
  bgun.py bootstrap gtest --name mytest product/myproductA
  bgun.py bootstrap template --name myTemplate product/myproductA

"""
import queue 
import threading 
from datetime import date
import getpass
import platform
import time
import difflib
from docopt import docopt
import subprocess
import json
from mako.template import Template
from mako import exceptions as makoExceptions
import pprint
import sys
import os
import re
import getpass
import shutil
import string
import glob
import copy
from pathlib import Path
from enum import Enum
import csv

#from test.support import resource
CONFIG_FILE="bgen.json"
JDEPENDENCY_FILE="jdependency.json"
JDATA_FILE="data.json"
DEPENDENCY_FILE_NAME="dependency.json"
VERSION_FILE_NAME="version.json"
DESCRIPTION_FILE_NAME="description.json"
SUPPORTED_COMPONENTS=['lib','module','product']
VERBOSE=False
FORCE=False
def GtestDependency():
    """ 
{
    "parentSrc":["*.cxx","*.cpp", "*.c"],
    "dependency":[
        {"type":"3rdParty",  "name": "gtest", "link_libraries":["gtest_main", "gtest"], "visibility":"private", "version":"${data["gtestVersion"]}"}
    ],
    "settings":{}
}
    """
def Git_h():
   """
/*******************************************************
* Copyright (C) ${data["year"]} Bgen
*
*  Automatically generated by bgen
*
*******************************************************/
#ifndef GIT_FILE_BGEN
#define GIT_FILE_BGEN
#ifdef __cplusplus
extern "C" {
#endif
struct BgenVerionInfo {
    const char *revision;
    const char *revisionShort;
    const char *bgen;
    const char *branch;
    const char *tag;
    const char *version;
    const char *user;
    const char *date;
};
#ifdef __cplusplus
}
#endif
#endif
   """

def Version_h():
   """
/*******************************************************
* Copyright (C) ${data["year"]} Bgen
*
*  Automatically generated by bgen
*
*******************************************************/

#ifndef VERSION_FILE_BGEN_${data["suffix"]}_
#define VERSION_FILE_BGEN_${data["suffix"]}_

#include "git/git.h"

#ifdef __cplusplus
extern "C" {
#endif
extern struct BgenVerionInfo versionInfo_${data["suffix"]};
#ifdef __cplusplus
}
#endif
#endif
   """

def Version_c():
   """

#include "git/git.h"
#include "git/gitDef.h"

#ifdef __cplusplus
extern "C" {
#endif
// ${data}
struct BgenVerionInfo versionInfo_${data["suffix"]} = {
                .revision = BGEN_REVISION(),
                .revisionShort = BGEN_REVISION_SHORT(),
                .bgen = BGEN(),
                .branch = BGEN_BRANCH(),
                .tag = BGEN_TAG(),
                .version = "${data["version"]}",
                .user = BGEN_USERNAME(),
                .date = BGEN_DATE()           
       };
const char* BgenInstallationSuffix_${data["suffix"]} = "${data["installationSuffix"]}";
#ifdef __cplusplus
}
#endif
   """

def GitDef_h():
   """
/*******************************************************
* Copyright (C) ${data["year"]} Bgen
*
*  Automatically generated by bgen
*  Created on 01/27/2021
*
*******************************************************/
#ifndef VERSION_FILE_BGEN_DEF__
#define VERSION_FILE_BGEN_DEF__
#define BGEN() "${data["BGEN_VERSION"]}"
#define BGEN_TAG() "${data["BGEN_TAG"]}"
#define BGEN_BRANCH() "${data["BGEN_BRANCH"]}"
#define BGEN_REVISION() "${data["BGEN_REVISION"]}"
#define BGEN_REVISION_SHORT() "${data["BGEN_REVISION_SHORT"]}"
#define BGEN_USERNAME() "${data["BGEN_USERNAME"]}"
#define BGEN_DATE() "${data["BGEN_DATE"]}"

#endif
   """
   
def EclipseIncludPath():
    """<?xml version="1.0" encoding="UTF-8"?>
<cdtprojectproperties>
<section name="org.eclipse.cdt.internal.ui.wizards.settingswizards.IncludePaths">
<language name="C++ Source File">
% for item in data["includePaths"]:
<includepath>${item}</includepath>
% endfor


</language>
<language name="C Source File">
% for item in data["includePaths"]:
<includepath>${item}</includepath>
% endfor

</language>
<language name="Object File">

</language>
<language name="Assembly Source File">

</language>
</section>
</cdtprojectproperties>
    """

def GtestSource():
    """
/*******************************************************
 * Copyright (C) 2020 bgen
 *
 * 
 *
 *  Created by bgen
 *      Author: bgen.py
 *******************************************************/

#include "gtest/gtest.h"
// #include "${data["parentName"]}/resource.h"

namespace {
class TestEntryPoint: public ::testing::Test {
 protected:
    TestEntryPoint() {
    }

    virtual ~TestEntryPoint() {
    }

    virtual void SetUp() {
    }

    virtual void TearDown() {
    }
};

TEST_F(TestEntryPoint, CanDoBar) {
    EXPECT_EQ(1, 1);
    ASSERT_EQ(2, 2);
    ASSERT_FLOAT_EQ(1.1, 1.1);
    ASSERT_DOUBLE_EQ(1.1, 1.1);
    ASSERT_NEAR(100, 100.01, 0.1);
    EXPECT_FLOAT_EQ(12.1, 12.1);
    EXPECT_DOUBLE_EQ(12.3, 12.3);
    EXPECT_NEAR(1, 1, 1);
}
}  // namespace


"""

def ProductDependency():
   """
{
     "platforms":["${data["arch"]}"],
     "dependency":[
         { "type":"system",  "name":"pthread"}
     ]
}
   """
   
def VersionPattern():
   """
{"version":"0.0.0"}
   """
def ProductResource1Example():
   """this is content of the example resource
   """
def ProductInstall1Example():
   """this is file to be installed
   """
   
def DatasetExample():
   """{
   "test":"some data ${data['nr']}"
}
   """

 
def TemplateExample():
   """${"<%"}
    import os
    print("****************************************************************************")
    print("                        {}".format(data['iFile']))
    print("****************************************************************************")
    print("   This is python code where you can preproces incoming data if needed")
    print("   Please note that some of properies has been aded already by bgen:")
    print("      Name of input file :       '{}'".format(data['iFile']))
    print("      Basename of input file :   '{}'".format(data['iName']))
    print("      Path for input file :      '{}'".format(data['iFile'])) 
    print("      Name of template file:     '{}'".format(data['tName']))
    print("      Basename of template file: '{}'".format(data['tPath']))
    print("      Path for template file:    '{}'".format(data['tFile']))
    print("      Name of output file:       '{}'".format(data['oFile']))
    print("      Basename of output file:   '{}'".format(data['oName']))
    print("      Path for output file:      '{}'".format(data['oPath']))
    print("\\n  In order to use this template, include it in the following way:")
    print('      #include "{}/{}/{}"'.format("${data["parentName"]}", data['tName'],data['oFile']))
    print("")
    # you can add here more python code
    data["custom"]="my custom data"
${"%>"}
void print_${"${data['iName']}"}() {
    printf("This is message from : ${"${data['iName']}"}\\n");
    printf("   data generated in template script:  ${"${data['custom']}"}\\n");
}

   """
   
def ProductSource():
    """
#include <stdio.h>
#include <string>
#include "${data["name"]}/resource.h"
 
int main(int argc, const char** argv) {
   printf("This is message from '${data["name"]}' product\\n");
   
   auto myResour = std::string(product_${data["name"]}_dir1_myResour_txt, product_${data["name"]}_dir1_myResour_txt_size);
   printf("  Resouruce content is: %s\\n", myResour.data());
   
}
    """
    
def ModuleLibSource():
    """
#include <stdio.h>
#include <string>
#include "${data["name"]}_module/resource.h"
#include "${data["name"]}/${data["name"]}.hpp"
int ${data["name"]}() {
   printf("This is message from '${data["name"]}' ${data["mode"]}\\n");
   
   auto myResour = std::string(module_${data["name"]}_dir1_myResour_txt, module_${data["name"]}_dir1_myResour_txt_size);
   printf("  Resouruce content is: %s\\n", myResour.data());
   return 0;
}
    """
    
def ModuleLibHeader():
    """
    
    #pragma once
    int ${data["name"]}() ;
    """
    
def getInstallationPrefix(runPath):
    return runPath + "/" + "dist"


def InstallReleaseInfo():
        """
#------------------------------------ INSTALL release info----------------------------------------
install(FILES ${data["runPath"]}/bgen/generated/${data["path"]}/${data["exetargetName"]}_releaseInfo.json DESTINATION  ${"${BGEN_INSTALLATION_SUFFIX}"}/bin)

"""

def VersionGenerator():
    """
#------------------------------------ VERSION ----------------------------------------------------
#we wont regenerate version always to trace git hash changes

add_custom_command(
    OUTPUT
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/version.h
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/version.c
       # vfake.c # fake ensure wa always run
    COMMAND ${data['bgen']} ver --input ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../version.json --installationSuffix ${data["installationSuffix"]} --suffix ${data['name']} --out ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/version
    DEPENDS ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../version.json
)


add_custom_target(${data["name"]}_version_gen
    DEPENDS
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/version.h
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/version.c
        #vfake.c
        )
        
"""

def ResourceGenerator():
    """
#------------------------------------ RESOURCE ---------------------------------------------------
% for item in data["resource"]:
add_custom_command(
    OUTPUT
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item["out"]}.h
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item["out"]}.c
    COMMAND  ${data['bgen']} res ${item["in"]} -o ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item["out"]}
    DEPENDS ${item["in"]}
)

% endfor

add_custom_target(${data["name"]}_resource_gen
    DEPENDS
% for item in data["resource"]:
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item["out"]}.h
        ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item["out"]}.c
% endfor
)

SET(RHEDERFILE ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/resource.h)
file(WRITE ${"$"}{RHEDERFILE} "#ifndef ResourceGenerator_${data['mode']}_${data['name']}\\n")
file(APPEND  ${"$"}{RHEDERFILE} "#define ResourceGenerator_${data["mode"]}_${data["name"]}\\n")
% for item in data["resource"]:
file(APPEND  ${"$"}{RHEDERFILE} "#include \\"../${item['name']}.h\\"\\n")
% endfor
file(APPEND  ${"$"}{RHEDERFILE} "#endif\\n")

"""

#todo unused function
def HeaderResource():
    """
#ifndef ResourceGenerator_${data["mode"]}_${data["name"]}
#define ResourceGenerator_${data["mode"]}_${data["name"]}
% for item in data["resource"]:
//${item["in"]} -> ${item["out"]}
#include "../${item['name']}.h"
% endfor

#endif
"""

#def cmakeSHscript():
#    """
##---------------------------------- SH SCRIPT --------------------------------------------------
#FILE(WRITE ${"${CMAKE_CURRENT_BINARY_DIR}"}/${data["name"]}.sh
#"Do not use .sh files\\n"
#"#!/bin/sh \\n"
#"echo \\".sh file is obsolete and will be removed soon. Please do not use .sh file.!!!!!!!!!!!!!!!!!!!!!!!!!!\\"\\n"
#"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\\n"
#"absolute=${"\$"}(dirname \\"${"$"}(readlink -f \\"${"$"}0\\")\\")\\n"
#"scriptName=${"\$"}(basename ${"\$"}0)\\n"
#"execName=${"\$"}{scriptName%.*}\\n"
#"export LD_LIBRARY_PATH=\\"${"\$"}absolute/../lib\\"\\n"
#"exec ${"\$"}absolute/${"\$"}execName ${"\$"}{@:1}\\n"
#)
#
#install(FILES ${"${CMAKE_CURRENT_BINARY_DIR}"}/${data["name"]}.sh DESTINATION ${"${BGEN_INSTALLATION_SUFFIX}"}/bin 
#     PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
#
#"""

def cmakeInit():
    """
#---------------------------------- INIT -------------------------------------------------------
% for item in data["cmakeDefines"]:
set(${item} ${data["cmakeDefines"][item]})
% endfor
% for item in data["cmakeList"]:
list(APPEND ${item} ${data["cmakeList"][item]})
% endfor
cmake_minimum_required(VERSION 3.10)
project(${data["name"]} VERSION ${data["version"]} LANGUAGES C CXX ASM)
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
    """
def cmakeTemplate():
    """
#---------------------------------- TEMPLATES ------------------------------------------------------------
% for item in data["templates"]:
execute_process(COMMAND ${data['bgen']} preprocess
                          --template  ${"$"}{CMAKE_CURRENT_SOURCE_DIR}/../template/${item["dir"]}/${item["template"]}  
                          --inputDir ${"$"}{CMAKE_CURRENT_SOURCE_DIR}/../template/${item["dir"]}/ 
                          --splitByName=${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data["name"]}/${item["dir"]}/{iName}_{tName}.hpp
                          --verbose
               RESULT_VARIABLE ${item["dir"]}_${item["template"]}_result)

if(NOT ${item["dir"]}_${item["template"]}_result EQUAL "0")
    message( FATAL_ERROR "Some error in ${"$"}{CMAKE_CURRENT_SOURCE_DIR}/../template/${item["dir"]}/${item["template"]}")
endif()

% endfor
    """

def cmakeDefinitions():
    """
#---------------------------------- DEFINITIONS ------------------------------------------------
% if data["settings"]["std"] != "":
set(CMAKE_CXX_STANDARD ${data["settings"]["std"]})
% endif
% if  data["settings"]["cxxflags"] != "":
set(CMAKE_CXX_FLAGS "${"${CMAKE_CXX_FLAGS}"} ${data["settings"]["cxxflags"]}")
% endif
% if "cxxflags" in data:
set(CMAKE_CXX_FLAGS "${"${CMAKE_CXX_FLAGS}"} ${data["cxxflags"]} ")
% endif
% if data["settings"]["cflags"] != "":
set(CMAKE_C_FLAGS "${"${CMAKE_C_FLAGS}"} ${data["settings"]["cflags"]}")
% endif
% if "cflags" in data:
set(CMAKE_C_FLAGS "${"${CMAKE_C_FLAGS}"} ${data["cflags"]} ")
% endif
% if data["settings"]["lflags"] != "":
set(CMAKE_EXE_LINKER_FLAGS "${"${CMAKE_EXE_LINKER_FLAGS}"} ${data["settings"]["lflags"]} ")
% endif
% if "lflags" in data:
set(CMAKE_EXE_LINKER_FLAGS "${"${CMAKE_EXE_LINKER_FLAGS}"} ${data["lflags"]} ")
% endif
set(BGEN_ARCH              "${"$ENV{BGEN_ARCH}"}")
set(BGEN_INSTALLATION_SUFFIX ${data["installationSuffix"]})
set(BGEN_RPATH              "${"$ORIGIN/:$ORIGIN/../lib/"}" )


% for option in data['jconfig']['options']:
% if data["BGEN_ARCH"] in data['jconfig']['options'][option]:
# ${option}
option(${option} "${data['jconfig']['options'][option]['description']}" ${data['jconfig']['options'][option]['default']})
if(${option})
message(INFO " ${data['jconfig']['options'][option]['description']} for '${data['name']}'")
% if 'cflags' in data['jconfig']['options'][option][data["BGEN_ARCH"]] != "":
   set(CMAKE_C_FLAGS "${"${CMAKE_C_FLAGS}"} ${data['jconfig']['options'][option][data["BGEN_ARCH"]]["cflags"]}")
% endif
% if 'cxxflags' in data['jconfig']['options'][option][data["BGEN_ARCH"]] != "":
   set(CMAKE_CXX_FLAGS "${"${CMAKE_CXX_FLAGS}"} ${data['jconfig']['options'][option][data["BGEN_ARCH"]]["cxxflags"]}")
% endif
% if 'lflags' in data['jconfig']['options'][option][data["BGEN_ARCH"]] != "":
   set(CMAKE_EXE_LINKER_FLAGS "${"${CMAKE_EXE_LINKER_FLAGS}"} ${data['jconfig']['options'][option][data["BGEN_ARCH"]]["lflags"]}")
% endif
endif()
% endif

% endfor

    """    

def cmakeAddProgramTarget():
    """
#---------------------------------- TARGET -----------------------------------------------------
if (CMAKE_GENERATOR MATCHES "Visual Studio")
#string(REPLACE "/MDd" "/MD" CMAKE_CXX_FLAGS_DEBUG "${"$"}{CMAKE_CXX_FLAGS_DEBUG}")
#string(REPLACE "/MDd" "/MD" CMAKE_C_FLAGS_DEBUG "${"$"}{CMAKE_C_FLAGS_DEBUG}")
endif()
file(GLOB_RECURSE SRC_FILES LIST_DIRECTORIES false ${" ".join(data["src"])}  ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.c ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.cpp ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.cxx)
% for item in data["sourceGeneratedByBgen"] + data["headerGeneratedByBgen"]:
 list(APPEND SRC_FILES ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item})
% endfor

if (NOT SRC_FILES)
    message(FATAL_ERROR "\nNo sources found in ${data["name"]}" )
endif()
add_executable(${data["exetargetName"]} 
   ${"${SRC_FILES}"}
%if data['objBasedHierarchy']:
% for spec in data['moduleSpecs']:
   $<TARGET_OBJECTS:${data['name']}_${spec['name']}_OBJ>
% endfor
%endif
)

% if data["generateVersion"] is True:
    add_dependencies(${data["exetargetName"]} ${data["name"]}_version_gen ) 
% endif
target_include_directories(${data["exetargetName"]} PUBLIC ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated)
target_include_directories(${data["exetargetName"]} PUBLIC ${" ".join(data["includeDirs"])})

% if "parentName" in data:
set(PARENT_BIN_DIR ${"$"}{CMAKE_CURRENT_BINARY_DIR}/../../../bin/)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
if (DEFINED CMAKE_BUILD_TYPE )
set(BUILD_TYPE ${"$"}{CMAKE_BUILD_TYPE})
else()
set(BUILD_TYPE "Release")
endif()
set(PARENT_OBJ_DIR ${"$"}{CMAKE_CURRENT_BINARY_DIR}/../../../bin/${data["parentObjTargetName"]}.dir/${"$"}{BUILD_TYPE}/)
else()
set(PARENT_OBJ_DIR ${"$"}{CMAKE_CURRENT_BINARY_DIR}/../../../bin/CMakeFiles/${data["parentObjTargetName"]}.dir/)
endif()

set(PARENT_SRC_DIR ${"$"}{CMAKE_CURRENT_SOURCE_DIR}/../../../src/)
target_include_directories(${data["exetargetName"]} PRIVATE ${"$"}{PARENT_SRC_DIR} ${"$"}{PARENT_SRC_DIR}public_headers ${"$"}{PARENT_BIN_DIR}generated)
add_dependencies(${data["exetargetName"]} ${data["parentObjTargetName"]} )

file(GLOB_RECURSE PARENT_SRC_FILES LIST_DIRECTORIES false ${" ".join(["${PARENT_SRC_DIR}" + s for s in data['parentSrc']])} ${"$"}{PARENT_BIN_DIR}generated/*.c ${"$"}{PARENT_BIN_DIR}generated/*.cpp ${"$"}{PARENT_BIN_DIR}generated/*.cxx)
% for item in data["parentSourceGeneratedByBgen"]:
 list(APPEND PARENT_SRC_FILES ${"$"}{PARENT_BIN_DIR}/${item})
% endfor


list(FILTER PARENT_SRC_FILES EXCLUDE REGEX .*/main.c)
list(FILTER PARENT_SRC_FILES EXCLUDE REGEX .*/main.cpp)
list(FILTER PARENT_SRC_FILES EXCLUDE REGEX .*/main.cxx)

foreach(item ${"$"}{PARENT_SRC_FILES})
        if (CMAKE_GENERATOR MATCHES "Visual Studio")
            string(REPLACE ${"$"}{PARENT_BIN_DIR} "" item ${"$"}{item})
            string(REPLACE ${"$"}{PARENT_SRC_DIR} "" item ${"$"}{item})
            get_filename_component(item ${"$"}{item} NAME)
            set(item ${"$"}{PARENT_OBJ_DIR}${"$"}{item})
            string(REGEX REPLACE "\\.[^.]*${"$"}" "" item ${"$"}{item})
        else()
            string(REPLACE ${"$"}{PARENT_BIN_DIR} ${"$"}{PARENT_OBJ_DIR} item ${"$"}{item})
            string(REPLACE ${"$"}{PARENT_SRC_DIR} ${"$"}{PARENT_OBJ_DIR} item ${"$"}{item})
        endif()

        set(obj ${"$"}{item}${"$"}{CMAKE_C_OUTPUT_EXTENSION})
        get_filename_component(obj ${"$"}{obj} ABSOLUTE)
        set_source_files_properties(${"$"}{obj} PROPERTIES GENERATED TRUE EXTERNAL_OBJECT TRUE)        
   list(APPEND PARENT_OBJ_FILES ${"$"}{obj})
endforeach()
target_sources(${data["exetargetName"]} PUBLIC ${"$"}{PARENT_OBJ_FILES})

% endif

set_target_properties(${data["exetargetName"]} PROPERTIES
       BUILD_WITH_INSTALL_RPATH TRUE
% if data["test"] is False:    
       INSTALL_RPATH_USE_LINK_PATH FALSE
       INSTALL_RPATH ${"${BGEN_RPATH}"}
% else:
       INSTALL_RPATH_USE_LINK_PATH TRUE      
% endif 
       )

"""
def cmakeInstallFromInstallDir():
    """
#----------------------------------- INSTALL FROM 'install' dir --------------------------------
% for item in data["install"]:
install(FILES "${item["source"]}" DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/${item["destination"]}"
     PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
% endfor
"""

def cmakeConfigFile():
    """
include(${"${CMAKE_CURRENT_LIST_DIR}"}/${data["name"]}.cmake)

file(GLOB_RECURSE ${data["name"]}_PUBLIC_LIBS   LIST_DIRECTORIES false  ${"${CMAKE_CURRENT_LIST_DIR}"}/../lib/*.so* ${"${CMAKE_CURRENT_LIST_DIR}"}/../lib/*.a*)
install(FILES ${"${"}${data["name"]}_PUBLIC_LIBS} DESTINATION ${"${BGEN_INSTALLATION_SUFFIX}"}/lib
     PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
"""
def cmakeLibTarget():
    """
#----------------------------------- TARGET ----------------------------------------------------

%if data['objBasedHierarchy'] == False:
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
%endif
file(GLOB_RECURSE SRC_FILES LIST_DIRECTORIES false ${" ".join(data["src"])} ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.c ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.cpp ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/*.cxx)
% for item in data["sourceGeneratedByBgen"]  + data["headerGeneratedByBgen"]:
 list(APPEND SRC_FILES ${"$"}{CMAKE_CURRENT_BINARY_DIR}/${item})
% endfor


add_library(object_${data["name"]} OBJECT ${"${SRC_FILES}"})
add_library(${data["otargetName"]} STATIC ${"$<TARGET_OBJECTS:"}object_${data["name"]}> )

% if data["generateVersion"] is True:
    add_dependencies(object_${data["name"]} ${data["name"]}_version_gen ) 
% endif



set_target_properties(${data["otargetName"]} PROPERTIES
       BUILD_WITH_INSTALL_RPATH TRUE
% if data["test"] is False:    
       INSTALL_RPATH_USE_LINK_PATH FALSE
       INSTALL_RPATH ${"${BGEN_RPATH}"}
% else:
       INSTALL_RPATH_USE_LINK_PATH TRUE
% endif 
       )


% if data["generateSharedLib"] is True:
add_library(${data["sotargetName"]} SHARED ${"$<TARGET_OBJECTS:"}object_${data["name"]}>)


set_target_properties(${data["sotargetName"]}  PROPERTIES
       BUILD_WITH_INSTALL_RPATH TRUE
% if data["noLibPrefix"] is True:
       PREFIX ""
% endif
% if data["noLibSuffix"] is True:
       SUFFIX ""
% endif
% if data["test"] is False:    
       INSTALL_RPATH_USE_LINK_PATH FALSE
       INSTALL_RPATH ${"${BGEN_RPATH}"}
% else:
       INSTALL_RPATH_USE_LINK_PATH TRUE       
% endif 
       )

set_property(TARGET ${data["sotargetName"]}  PROPERTY POSITION_INDEPENDENT_CODE ON)

set(${data["name"]}_VERSION ${"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"})
set(${data['name']}_SOVERSION ${"${PROJECT_VERSION_MAJOR}"})
set_target_properties(${data["sotargetName"]} PROPERTIES VERSION ${"${"}${data['name']}_VERSION} SOVERSION ${"${"}${data['name']}_SOVERSION})
% endif

#for objects build
target_include_directories(object_${data["name"]} PRIVATE ${" ".join(data["includeDirs"])})
target_include_directories(object_${data["name"]} PUBLIC ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated)

    """
    


def cmakePublishIncludes():
    """
#----------------------------------- INCLUDES --------------------------------------------------
#to know what need to be installed
file(GLOB_RECURSE globedHeaders LIST_DIRECTORIES false ./public_headers/*.hpp ./public_headers/*.h)
% if len(data["resource"]) > 0:
list(APPEND globedHeaders ${"$"}{CMAKE_CURRENT_BINARY_DIR}/generated/${data['name']}/resource.h)
% endif  
   
% for t in data["mainTargets"]:
set_target_properties(${t}  PROPERTIES PUBLIC_HEADER "${"${globedHeaders}"}")
% endfor


#for subdirectory
% for t in data["mainTargets"]:
target_include_directories(${t} PUBLIC ${"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}"}/public_headers/>)
target_include_directories(${t} PUBLIC PUBLIC ${"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}"}/generated/>)  

% endfor
#for findpackage
% for t in data["mainTargets"]:
target_include_directories(${t} PUBLIC ${"$<INSTALL_INTERFACE:${BGEN_INSTALLATION_SUFFIX}"}/include>)
% endfor
    """
    

def cmakePackageDependency(): #not used, not tested
    """
#---------------------------------- PACKAGE DEPENDENCY -----------------------------------------
% for spec in data['packageSpecs']:
find_package(${spec["name"]} ${spec["version"]} EXACT REQUIRED)

% for lib in spec["targets"]:
% for t in data["mainTargets"]:
target_link_libraries(${t} ${spec["visibility"]} ${lib})
% endfor
% endfor
% endfor

"""


def cmakeSubmodule():#todo: ded code to remove
    """
#----------------------------------- SUBDIR -----------------------------------------------------
% for spec in data['moduleSpecs']:
#add_subdirectory(${"${PROJECT_SOURCE_DIR}"}/../../module/${spec["name"]} ${spec["name"]})
% endfor
"""

def cmakeSubmoduleDependency():
    """
#----------------------------------- SUBDIR DEPENDENCY ------------------------------------------
%if data['objBasedHierarchy']:
# obj Based Hierarchy

target_include_directories(${data["exetargetName"]} PUBLIC
% for spec2 in data['moduleSpecs']:
    ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec2['name']}/src/public_headers
% endfor
)

% for spec in data['moduleSpecs']:

file(GLOB_RECURSE ${data['name']}_${spec['name']}_SRC LIST_DIRECTORIES false 
   ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src/*.cpp
   ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src/*.c
   ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src/*.cxx
   ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src/*.s
)

add_library(${data['name']}_${spec['name']}_OBJ OBJECT 
    ${'$'}{${data['name']}_${spec['name']}_SRC}
)

target_include_directories(${data['name']}_${spec['name']}_OBJ PUBLIC 
#    ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src/public_headers
    ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec['name']}/src    
)
target_include_directories(${data['name']}_${spec['name']}_OBJ PUBLIC
% for spec2 in data['moduleSpecs']:
    ${'$'}{CMAKE_CURRENT_SOURCE_DIR}/../../../module/${spec2['name']}/src/public_headers
% endfor
)

% endfor
% else:

% for spec in data['moduleSpecs']:
#${spec}
% for lib in spec["targets"]:
% for t in data["mainTargets"]+data["intermediateTargets"]:
target_link_libraries(${t} ${spec["visibility"]} ${lib})
% endfor
% endfor
% endfor
% endif
"""
def cmakeSystemDependency():
    """
#----------------------------------- SYSTEM DEPENDENCY ------------------------------------------
% for spec in data['systemDepSpecs']:
% for t in data["mainTargets"]+data["intermediateTargets"]:
% if spec["findMethod"] == "find_package":
   find_package(${spec["name"]} REQUIRED)
   target_link_libraries(${t} PRIVATE ${spec["target"]})
% else:
   target_link_libraries(${t} ${spec["visibility"]}  ${spec["name"]})
% endif
% endfor
% endfor
"""
    
def cmakeCPack():
    """
#----------------------------------- CPACK ------------------------------------------------------   
set(CPACK_GENERATOR "DEB")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${data["companyName"]}") 
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_DEBUG ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
set(CPACK_SET_DESTDIR true)
set(CPACK_INSTALL_PREFIX /opt/${data["companyName"]}_DEB_PACKAGE_PREFIX)
INCLUDE(CPack)
    """
    
def cmakeInstall():
    """
#----------------------------------- INSTALL ----------------------------------------------------    
install(TARGETS ${" ".join(data["mainTargets"])}   
    RUNTIME       DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/bin"
    LIBRARY       DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/${data["installLibPrefix"]}" 
    ARCHIVE       DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/${data["installLibPrefix"]}"
    % if data["installIncludes"] is True:         
    PUBLIC_HEADER DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/include/${data["name"]}"
    INCLUDES      DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/include/${data["name"]}"
    % endif
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE
    )        
    
% if data["logic"]["setenv"] is True:
set(PATHENV ${"$"}{PATHENV}:${data['installationPrefix']}/${"$"}{BGEN_INSTALLATION_SUFFIX}/bin PARENT_SCOPE)
set(BIN ${"$"}{BIN} ${data['installationPrefix']}/${"$"}{BGEN_INSTALLATION_SUFFIX}/bin/${data["name"]}.sh PARENT_SCOPE)
% endif

      
    """
    
def cmakeInstallDependency():
    """
#----------------------------------- INSTALL DEPENDENCY -----------------------------------------        
% for lib in data['libSpecs']:
% for file_to_install in data['libSpecs'][lib]['install_list']:
install(FILES ${file_to_install} DESTINATION ${"${BGEN_INSTALLATION_SUFFIX}"}/${data["installLibPrefix"]}
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
% endfor
% for item_to_install in data['libSpecs'][lib]['install']:
install(FILES ${item_to_install["src"]} DESTINATION ${"$"}{BGEN_INSTALLATION_SUFFIX}/${item_to_install["des"]}
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)     
% endfor
% endfor

#runtime libs:
% for RunTimelib in data['runTimelibraries']:
install(FILES ${RunTimelib} DESTINATION ${"${BGEN_INSTALLATION_SUFFIX}"}/${data["installLibPrefix"]}
     PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
% endfor

#install(FILES ${"$"}{CMAKE_BINARY_DIR}/generated/git/git.json DESTINATION ${"${BGEN_INSTALLATION_SUFFIX}"}/bin)
    """
   
def cmake3rdParty():
    """#----------------------------------- 3RDPARTY----------------------------------------------------  
% for lib in data['libSpecs']:
#${lib}
% for include_directories in data['libSpecs'][lib]['include_directories']:
% for t in data["mainTargets"]+data["intermediateTargets"]:
target_include_directories(${t} ${data['libSpecs'][lib]['visibility']} ${include_directories})
% endfor
% endfor
% for link_directories in data['libSpecs'][lib]['link_directories']:
% for t in data["mainTargets"]+data["intermediateTargets"]:
target_link_directories(${t} ${data['libSpecs'][lib]['visibility']} ${link_directories})
% endfor
% endfor
% for t in data["mainTargets"]:
% for libName in data['libSpecs'][lib]["libs"]:
target_link_libraries(${t} ${data['libSpecs'][lib]['visibility']} ${libName})
% endfor
% endfor
% endfor
"""
    
def userInclude():
    """
#----------------------------------- ${data["title"]} -----------------------------------------------
#${data["fileName"]}
"""    

def cmakeRoot():
    """
% for item in data["cmakeDefines"]:
set(${item} ${data["cmakeDefines"][item]})
% endfor

cmake_minimum_required(VERSION 3.10)
add_definitions(-DBGEN_ARCH_${data['BGEN_ARCH']})
project(bgen)
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
#set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${"${CMAKE_COMMAND}"} -E time")
#set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${"${CMAKE_COMMAND}"} -E time")

IF((NOT DEFINED CMAKE_INSTALL_PREFIX) OR CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  SET(CMAKE_INSTALL_PREFIX "${data['installationPrefix']}" CACHE PATH "Default install path" FORCE)
ENDIF()


if(NOT  DEFINED ENV{BGEN_ARCH} )
   message(FATAL_ERROR "The BGEN_ARCH environemnt variable is not defined.Use command: 
      export BGEN_ARCH=myAbiOS" )
endif()

set(PATHENV PATH=\\${"$"}PATH)

% for item in data["cmakes"]:
% if "skipWhenSet" in item and len(item["skipWhenSet"]) > 0:
if (${" OR ".join(item["skipWhenSet"])})
message(INFO " Asset '${item['assetPath']}' skipped from compilation.")
else()
%endif


% if "data" in  data["jdependency"][item["jpath"]]:
add_subdirectory(${item["dir"]} ${item["path"]})
% for target in data["jdependency"][item["jpath"]]["data"]["mainTargets"] + data["jdependency"][item["jpath"]]["data"]["intermediateTargets"]:
add_dependencies(${target} global_git_gen)
target_include_directories(${target} PRIVATE ${"$"}{CMAKE_BINARY_DIR}/generated)
% endfor
% if "skipWhenSet" in item and len(item["skipWhenSet"]) > 0:
endif()
% endif
% endif
% endfor

% if data['targetSystem'] != "WINDOWS":
FILE(WRITE ${"$"}{CMAKE_CURRENT_BINARY_DIR}/envsetup.sh ${"$"}{PATHENV})

ADD_CUSTOM_TARGET(links_create ALL)
ADD_CUSTOM_COMMAND(TARGET links_create COMMAND mkdir -p ${"$"}{CMAKE_CURRENT_BINARY_DIR}/bin/${"$"}ENV{BGEN_ARCH})

foreach(item ${"$"}{BIN})
  get_filename_component(destination ${"$"}{item} NAME)
  ADD_CUSTOM_COMMAND(TARGET links_create COMMAND ln -sf ${"$"}{item} ${"$"}{CMAKE_CURRENT_BINARY_DIR}/bin/$ENV{BGEN_ARCH}/${"$"}{destination})
endforeach(item)
% endif

add_custom_target(global_git_gen
    COMMAND ${data['bgen']} ver --out ${"$"}{CMAKE_BINARY_DIR}/generated/git/git --git
    WORKING_DIRECTORY ${"$"}{CMAKE_SOURCE_DIR}
    COMMENT "Generate git revision info 'git.h'"
)
""" 
    

def jsonLibDescription():
    """
#----------------------------------- DESCRIPTION FILE -------------------------------------------
FILE(WRITE ${"${CMAKE_CURRENT_BINARY_DIR}"}/description.json
"{\\"include_directories\\":[\\"include\\"],\\"link_directories\\":[\\"lib\\"],\\"dependency\\":${data["libRalation"]}}"
)

install(FILES "${"${CMAKE_CURRENT_BINARY_DIR}"}/description.json" DESTINATION "${"${BGEN_INSTALLATION_SUFFIX}"}/bgen"
    PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ  GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE)
"""

def getUserName():
    try:
        username = getpass.getuser()
    except:
        username = os.getuid()
    return username


class VersionStatus(Enum):
    EXIST = 1
    OLDER = 2
    NEWEST = 3
    NOT_EXIST = 4

def read_output(pipe, funcs):
    
    line = pipe.readline()
    while line:
        line = pipe.readline()
        for func in funcs:
            func(line)

    #or line in iter(pipe.readline, b''):
    #   for func in funcs:
    #       func("!x!"+line)
    pipe.close()


def write_output(get):
    for line in iter(get, None):
        sys.stdout.write(line)


def run_cmd(command, cwd=None):
    outs, errs = None, None

    proc = subprocess.Popen(
        command,
        cwd=cwd,
        universal_newlines=True,
        shell=False,
        close_fds=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        bufsize=1
        )


    outs, errs = [], []

    q = queue.Queue()

    stdout_thread = threading.Thread(
            target=read_output, args=(proc.stdout, [q.put, outs.append])
            )

    stderr_thread = threading.Thread(
            target=read_output, args=(proc.stderr, [q.put, errs.append])
            )

    writer_thread = threading.Thread(
            target=write_output, args=(q.get,)
            )

    for t in (stdout_thread, stderr_thread, writer_thread):
        t.daemon = True
        t.start()
    proc.wait()

    for t in (stdout_thread, stderr_thread):
        t.join()
    q.put(None)

    rc = proc.returncode

    return (rc, (outs, errs))

 
class ReleaseDrive:
    def __init__(self, rootPath):
        self.rootPath = rootPath
        self.versionCashe={}
    def constructDir(self, path, arch, version):
        mode = getComponentType(path)
        name = os.path.basename(path)
        return path + "/" + name + "-"+version+"/"+arch
    
    def isVersionExist(self, path, arch,version):
        mode = getComponentType(path)
        name = os.path.basename(path)
        path = mode + "/" + name
        return os.path.isdir(self.rootPath + "/" +path+"/"+name+"-"+version)
          
    def getVersions(self, path, arch):
        #------------------fix me ----------
        mode = getComponentType(path)
        name = os.path.basename(path)
        path = mode + "/" + name
        #----------------------------------

        if path in self.versionCashe:
            return self.versionCashe[path]
        self.versionCashe[path]=[]
        pathToAsset = self.rootPath + "/" +path

        if os.path.isdir(self.rootPath + "/" +path):
            for v in os.listdir(pathToAsset):
                if not os.path.isdir(pathToAsset + "/" + v + "/" + arch):
                    continue
                matchObj = re.match( r'(' + name + ')-(.+)', v)
                if matchObj:
                    self.versionCashe[path].append(matchObj.group(2))

        self.versionCashe[path].sort(key=lambda s: list(map(int, s.split('.'))))        
        return self.versionCashe[path]


    def checkVersion(self, path, version, arch):
        return VersionStatus.NOT_EXIST

def generateJsonLibDescription(jdependency, path, data):
    jdescription={"dependency":[]}

    jdescription["dependency"]=  jdescription["dependency"] +  jdependency[path]["dependency"]

    if data['targetSystem'] == "WINDOWS":
        jdescription[data["sotargetName"]] = [".."+"/"+"bin"+"/"+"lib"+data["sotargetName"]+".dll"]

    jdescription["include_directories"]   =  ['include']
    jdescription["link_directories"]       =  ['lib']
    jdescription["link"] =  []

    if data['targetSystem'] == "WINDOWS":
        jdescription["link"].append({data["sotargetName"]:[".."+"/"+"bin"+"/"+"lib"+data["sotargetName"]+".dll"]})

    newContent = json.JSONEncoder(indent=4).encode(jdescription)
    writeFileIfChanged(DESCRIPTION_FILE_NAME+"x", newContent)

def generateUserIncludes(agregat, data, path, title, init):
    pathToCmakeDir = path+"/"+"cmake"
    if not os.path.isdir(pathToCmakeDir):
        return
    for f in os.listdir(pathToCmakeDir):
        if init and f != "init.cmake":
            continue
        if not init and f == "init.cmake":
            continue
        agregat.append({"t":Template(userInclude.__doc__),"data":{"fileName":pathToCmakeDir+"/"+f,"title":title},"name":"userInclude"})
        with open (pathToCmakeDir+"/"+f, "r") as fileHandler:
            fileContent=fileHandler.read()
            agregat.append({"t":Template(fileContent),"data":data,"name":f})       


def matchLib(lib, path,fileLib):
    for file in os.listdir(path):
        if (file == lib):
            fileLib = file
            break
        if (file == "lib"+lib+".dll"):
            fileLib = file
            break
        if (file == "lib"+lib+".dll.a"):
            fileLib = file
            break
        if (file == lib+".dll"):
            fileLib = file
            break
        if (file == lib+".dll.a"):
            fileLib = file
            break
        if (file == "lib"+lib+".so"):
            fileLib = file
            break
        if (file == "lib"+lib+".a"):
            fileLib = file
            break
        if (file == "lib"+lib+".lib"):
            fileLib = file
            lib = fileLib
            break
        if (file == lib+".lib"):
            fileLib = file
            break
    return [fileLib , lib]

def findLib(pathToLibrary, description, lib, buildType, skipstaticinstall):    
    install_list=[]
    fileLib = None
    for ldir in  description['link_directories']:
        if (fileLib != None):
            break
        path = pathToLibrary+'/'+ldir;       
        if buildType == "Debug":
            debugLib, file_extension = os.path.splitext(lib)
            debugLib = debugLib + "d" + file_extension
            fileLib, debugLib =  matchLib(debugLib, path,fileLib)
            if fileLib != None:
               lib = debugLib
               break

        if fileLib == None:
            fileLib, lib =  matchLib(lib, path,fileLib)
            if fileLib != None:
                break

    if (fileLib == None):
        print("Error: unable to find '{}' lib in '{} ({})'.".format(lib,pathToLibrary,description['link_directories']))
        exit(1)
        #name, ext = os.path.splitext(fileLib)   
    
    if "link" in description:
        linkKey = "link"
    else:
        linkKey = "dependency"
    if linkKey in description:      
        #if linkKey == "dependency":
        #    print("Warning: the 'dependency' property should be changed into 'link' in the {}/bgen/description.json".format(pathToLibrary))
        for item in [lib,fileLib]:
            if item in description[linkKey]:
                for dep in description[linkKey][item]:
                    fileToInstall = path + "/" + dep
                    if not os.path.isfile(fileToInstall):
                        fileToInstall2 = pathToLibrary + "/" + dep                                     
                        if not os.path.isfile(fileToInstall2):
                            print("Error: cannot find the '{}' file requred by the '{}' lib in the '{}' link section.".format(dep, pathToLibrary, item))          
                            print("   The following Path has been checked:");
                            print("      {} - file not found".format(fileToInstall));
                            print("      {} - file not found".format(fileToInstall2));                                                        
                            exit(1)
                        fileToInstall = fileToInstall2

                    install_list.append(fileToInstall)
                    aliases = findLibAlias(path,dep)
                    install_list =  install_list + aliases


#find alias lib.a.1 ,lib.a.1.2, ...
    aliases = findLibAlias(path, fileLib)
    install_list =  install_list + aliases

    install_list.append(path+"/"+fileLib)
    filteredInstallList=[]
    for item in install_list:
        name,ext = os.path.splitext(item)
        if skipstaticinstall and (ext == ".lib" or ext == ".a"):
            #print("skip {}".format(item))
            continue
        filteredInstallList.append(item)

    #todo: add pdb to install    
       
    #installFiles.append(path+'/'+fileLib)
    #arrt = {'type':"?",'ext':ext,'link_path':path,'install_list':install_list}
    arrt = {'install_list':filteredInstallList,'link_with':lib,'fileLib':fileLib}
 
    #if ext == ".so" or ext == ".dll":
    #    arrt['type'] = "shared"
    #else:
    #    arrt['type'] = "static"
    return arrt
        
def shellCmd(command, quiet=False):    
    p1 = subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)   
    result,error = p1.communicate()   
    if (len(error) != 0) and (quiet == False):
        print(error)  
    if (p1.wait() ):
        #print("ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        if not quiet:
            print("Error while executing command:'{}'".format(command))
            print("   in the working dir:'{}'".format(os.getcwd()))
            exit(1)
        else:
            return None
    return result.strip().decode("utf-8") 
   
   
   
def addPckageDependency(dependency, attr): 
    if ("packageLibs" not in dependency) or (len(dependency["packageLibs"]) == 0):
        dependency["packageLibs"] = ["shared_"+dependency["name"]]    
    spec={"packageVersion":dependency["version"], "name":dependency["name"],"targets":dependency["packageLibs"], "visibility":dependency["visibility"].upper()}                                                        
    attr.append(spec);
       
def getModuleTargetName(name):
    return name+"_module"


        
def addSubModuleDependency(dependency, attr):              
    if ("version" in dependency):
        print("Warning: property 'version' is not used for the dependency type {}, and will be ignored.",dependency["type"])    
                    
    if ("packageLibs" in dependency):
        print("Warning: property 'packageLibs' is not used for the dependency type {}, and will be ignored.",dependency["type"])
        
#"object_"+dependency["name"]
    targetName = getModuleTargetName(dependency["name"])
    spec={"name":dependency["name"], "targets":["static_"+targetName], "visibility":dependency["visibility"].upper()}
    
    attr.append(spec);
    
def addSystemDependency(dependency, attr):
    if ("version" in dependency):
        print("Warning: property 'version' is not used for the dependency type {}, and will be ignored.",dependency["type"])
    
    if ("packageLibs" in dependency):
        print("Warning: property 'packageLibs' is not used for the dependency type {}, and will be ignored.",dependency["type"])
    findMethod = None
    target= None
    if "findMethod" in dependency:
        target = "{}::{}".format(dependency["name"],dependency["name"])
        if "target" in dependency:
            target = dependency["target"]

        findMethod = dependency["findMethod"]
    spec={"name":dependency["name"], "visibility":dependency["visibility"].upper(),"findMethod":findMethod,"target":target}   
    attr.append(spec);

#find alias lib.a.1 ,lib.a.1.2, ...
def findLibAlias(path,fileLib):
    #find target
    target = path+"/"+fileLib
    res=[]
    if (os.path.islink(target)):
        target =  os.path.realpath(target)
    for file in os.listdir(path):
        testFile = path+"/"+file
        if (os.path.islink(testFile)):
            realFile = os.path.realpath(testFile)
            if realFile == target :                     
                res.append(testFile)
    res.append(target)

    #remove in the future
    name, ext = os.path.splitext(fileLib)
    res=[]
    for file in os.listdir(path):
        if file != fileLib:
            matchObj = re.match( name+'(.a|.so).*', file, 0)
            if (matchObj):
                res.append(path+"/"+file)
    return res
    
def addLibDependency(dependency, dirName, attr, buildType, skipstaticinstall):
    if 'BGEN_RELEASE_PATH' not in os.environ:
        print("Error: If you need to use {} in the dependency file, you must define the BGEN_RELEASE_PATH environment variable.".format(dirName))
        exit(1)


    pathToLib = os.environ['BGEN_RELEASE_PATH']+"/" + dirName + "/"+dependency['name']+"/"+dependency['name']+"-"+dependency['version']+"/"+os.environ["BGEN_ARCH"]
    pathToDef = pathToLib + "/bgen/"+DESCRIPTION_FILE_NAME          
    if  'visibility' not in dependency:
        visibility = 'PRIVATE'
    else:
        visibility = dependency['visibility'].upper()    

    if ('link_libraries' not in dependency):
        dependency['link_libraries']=[dependency['name']];    
    try:
        with open(pathToDef, 'r') as f:
            description = json.load(f)
    except IOError:
        print ("Error: Could not read file {}:".format(pathToDef))
        exit(1)
    except ValueError as e:
        print ("Error: wrong json format in file: '{}'".format(pathToDef))
        print(e)
        exit(1)    
    include_directories=[]
    if ('include_directories' in description):
        include_directories = [pathToLib+"/"+s for s in description['include_directories']]   

    #arrt = {'type':"?",'ext':ext,'lib_directories':path,'alias_path':alias_path}
    spec = {'path':pathToLib,'install_list':[],"install":[],'link_directories':[],'include_directories':include_directories,'visibility':visibility,'libs':[],'version':'?'}

    if(dependency['name'] in attr):
        spec = attr[dependency['name']]
        if (dependency['version'] != attr[dependency['name']]['version']):
            print("Error: Dependency mismach. Try to merge dependency with difrent version {} and {} for the '{}' asset".format(dependency['version'],attr[dependency['name']]['version'],dependency['name']))
            exit(1)      
    else:
        attr[dependency['name']]=spec
        spec['version'] = dependency['version']   

    for lib in dependency['link_libraries']:
        value = findLib(pathToLib, description, lib, buildType, skipstaticinstall)    
        if "install" in description:
            for triger in [lib,value["fileLib"]]:
                if triger in description["install"]:
                    if VERBOSE:
                        print("triger '{}' found".format(triger))
                    for installItem in description["install"][triger]:
                        for item in installItem:
                            src = pathToLib+"/"+item                
                            des = installItem[item]
                            if (os.path.isdir(src)):
                                #for fileItem in glob.glob(src+"/**/*", recursive=True):
                                try:
                                    for posixPath in Path(src).rglob('*'):
                                        fileItem = posixPath.as_posix()
                                        fileItem = fileItem.replace("\\", "/")
                                        if os.path.isfile(fileItem):   
                                            installItem = {"src":fileItem,"des":os.path.dirname(des+fileItem[len(src):])}                        
                                            spec['install'].append(installItem)
                                            if VERBOSE:
                                                print("add '{}' file to install.".format(installItem["src"])) 
                                        else:
                                            if VERBOSE:
                                                print("Skipping '{}' because it is not a file.".format(fileItem)) 
                                except OSError as e:
                                    print("Error: cannot access '{}' file(s)".format(src))
                                    print(e)
                                    exit(1)
                            else:  
                                spec['install'].append({"src":src,"des":des})      
                                
                    for item in spec['install']:
                        if (not os.path.isfile(item["src"])):
                                print("Error: unable to install the '{}' file, requred by the '{}' lib. File not found".format(item["src"],pathToLib))
                                exit(1)
                        else:
                            if VERBOSE:
                                print("Info: add for install: {} -> {}".format(item["src"],item["des"]));
                else:
                    if VERBOSE:
                        print("triger '{}' not found in '{}'".format(triger, description["install"]))            
        for item in value['install_list']:
            if (item not in spec['install_list']):
                spec['install_list'].append(item)
        
        if value['link_with'] not in spec['libs']:
            spec['libs'].append(value['link_with'])
        
        for ldir in  description['link_directories']:
            path = pathToLib + '/' + ldir
            if path not in spec['link_directories']:
                spec['link_directories'].append(path)
      

    if "runtime" in description:
        for runtime in description["runtime"]:
            fullPathToLib = pathToLib+"/"+runtime
            if fullPathToLib not in spec['install_list']:
                spec['install_list'].append(fullPathToLib)
            fileLib = os.path.basename(fullPathToLib)
            pathLib = os.path.dirname(fullPathToLib)
            for item in findLibAlias(pathLib,fileLib):
                if item not in  spec['install_list']:
                    spec['install_list'].append(item)   
   

 

def versionReader(path):
    versionString = readFileFromGit(path)
    jversion={}
    try: 
        jversion = json.loads(versionString)
    except ValueError as e:
        print ("Error: wrong json format in file: '{}'".format(path))
        print(e)
        exit(1) 
    return jversion

def readSetsFromEnv(data):
    for item in os.environ:
        matchObj = re.match( r'BGEN_SET_(\S+)', item)
        if matchObj:
            data['cmakeDefines'][matchObj.group(1)]=os.environ[matchObj.group(0)]

        matchObj = re.match( r'BGEN_APPEND_(\S+)', item)
        if matchObj:
            data['cmakeList'][matchObj.group(1)]=os.environ[matchObj.group(0)]
            
            
def readRunTimeLibraries(data):
    if "BGEN_RUNTIME_LIB" in os.environ:
        data['runTimelibraries'] = os.environ["BGEN_RUNTIME_LIB"].split(",")

def getTestRootSuffix():    
    return "test"

def getInstallationSuffix(path,version,arch,test):
    assetName = os.path.basename(path) 
    suffix = path + "/"+ assetName+ "-" + version + "/" + arch
    if test:
        suffix = getTestRootSuffix() + "/" + suffix;
    return suffix

def writeFileIfChanged(FileName, newContent):
    dirName = os.path.dirname(FileName)  
    if (dirName):  
        os.makedirs(dirName, exist_ok=True)

    
    oldContent="" 
    if os.path.isfile(FileName):
        oldContent = open(FileName, 'r').read()
    if (oldContent != newContent) or FORCE:
        print("Write file: '{}'".format(FileName))

        f = open(FileName,"w+")
        f.write(newContent)
        f.close()
    else:
        if VERBOSE:
            print("Skip file writing: {}. No change in file requred.".format(FileName))


def processTempates(FileName, agregat):
    if FileName != "":
        if not os.path.isabs(str(FileName)):      
            FileName = os.path.abspath(os.getcwd()+"/"+str(FileName)) 

        dirname = os.path.dirname(FileName)
        if dirname and not os.path.isdir(dirname):
            os.makedirs(dirname, exist_ok=True)

    newContent = ""
    for template in agregat:
        try: 
            newContent = newContent + template["t"].render(data=template["data"])
        except NameError as e:
            print ("Error[1]: in template: '{}'".format(template["name"]))
            print (makoExceptions.text_error_template().render())
            print(e)
            exit(1)
        except TypeError as e:
            print ("Error[2]: in template: '{}'".format(template["name"]))
            print (makoExceptions.text_error_template().render())
            print(e)
            exit(1)
        except KeyError as e:
            print ("Error[3]:(KeyError) in tamplate: '{}'".format(template["name"]))
            print (makoExceptions.text_error_template().render())
            
            print(e)
            exit(1)

    if FileName != "":
        writeFileIfChanged(FileName,newContent)
    else:
        print(newContent)


def resCompiler(fileName, outputName):
    CoutputFileName = outputName+".c" 
    HoutputFileName = outputName+".h"
   
    name = os.path.basename(outputName)
    path = os.path.dirname(outputName)    
    if path != '' : 
        if not os.path.isdir(path):
            print("Create directory{}.".format(path))
            os.makedirs(path, exist_ok=True)

#        writeFileIfChanged(FileName,newContent)
    newContent = ""
    newContent = newContent + "#ifndef ResourceGenerator_{}\n".format(name)
    newContent = newContent + "#define ResourceGenerator_{}\n".format(name)
    newContent = newContent + "#ifdef __cplusplus\n"
    newContent = newContent + 'extern "C" {\n'
    newContent = newContent + "#endif\n"
    newContent = newContent + "extern const char {}[];\n".format(name)
    newContent = newContent + "extern const unsigned int  {}_size;\n".format(name)
    newContent = newContent + "#ifdef __cplusplus\n"
    newContent = newContent + "}\n"
    newContent = newContent + "#endif\n"
    newContent = newContent + "#endif"
   
    writeFileIfChanged(HoutputFileName, newContent)
    newContent = "const char {}[] = {}\n    ".format(name,"{")
    size = 0;
    comma = ""
    inx = 0

    with open(fileName, "rb") as f:
        byte = f.read(1)
        comma = ""
        inx = 0
        while byte != b"":
            newContent = newContent + "{}".format(comma)
            if (inx > 16):
                inx = 0;
                newContent = newContent + "\n    "
            newContent = newContent + "0x{}".format(byte.hex())
            size = size + 1
            comma = ", "
            inx = inx +1            
            byte = f.read(1)
    
    newContent = newContent + "{}".format(comma)
    if (inx > 16):
        newContent = newContent + "\n    "
    newContent = newContent + "0x00"
    size = size + 1
            
    newContent = newContent + "\n};\n"

    newContent = newContent + "const unsigned int {} = {};\n    ".format(name+"_size",size)
    f.close()

    writeFileIfChanged(CoutputFileName, newContent)    
    
def loadConfig(rootPath):
    fileName = rootPath + "/" + CONFIG_FILE
    if not os.path.isfile(fileName):
        return {"options":{}}
    try: 
        
        f = open(fileName, 'r')
        content = f.read()        
        return json.loads(content)              
    except IOError:
        print ("Error: Could not read file:", fileName)
        exit(1)
    except ValueError as e:
        print ("Error: wrong json format in file: '{}'".format(fileName))
        print(e)
        exit(1)
      
      
def mergejconfig(parent, child):
    #localConfig = loadConfig(os.getcwd())
    jconfig = copy.deepcopy(parent)
    for option in child["options"]:        
        if option not in jconfig["options"]:
            jconfig["options"][option] = {}
        for item in child["options"][option]:
            jconfig["options"][option][item] = child["options"][option][item]
    return jconfig

def getAllDependency(data):  
    dlist={}
    for prop in data:           
        if prop in ["derived_dependency"]:            
            for value in data[prop]:               
                if (value not in dlist):    
                    dlist[value] = {"type":os.path.dirname(value),"name":os.path.basename(value)}
        if prop in ["dependency"]:           
            for value in data[prop]:
                strValue = "{}/{}".format(value["type"], value["name"])
                if (strValue not in dlist):
                    dlist[strValue] = {"type":value["type"],"name":value["name"]}                                  
    return dlist;
           
   
def generateCmakeInCurrentDirectory(path, jdependency, runPath, rootPath, buildType, skipstaticinstall, skipVersionCheck):
    print("Generate config for {}".format(path))
    logic={'product': {'template':True,'installReleaseInfo':True,'generateVersion':True, 'setenv':True,  'installDependency':True ,'init':True, 'definition':True, 'installFromInstallDir':True, 'programTarget':True, 'libTarget':False, 'publishIncludes':False, 'dependency':True,'Cpack':True, 'Install':True, 'export':False,'generateStaticLib':False,'generateSharedLib':False, 'generateJDescription':False},
           'module':  {'template':True,'installReleaseInfo':False,'generateVersion':True, 'setenv':False, 'installDependency':False,'init':True, 'definition':True, 'installFromInstallDir':False, 'programTarget':False,'libTarget':True,  'publishIncludes':True,  'dependency':True,'Cpack':False,'Install':False, 'export':False,'generateStaticLib':True, 'generateSharedLib':False, 'generateJDescription':False},
           'lib':     {'template':True,'installReleaseInfo':True,'generateVersion':True, 'setenv':False, 'installDependency':True ,'init':True, 'definition':True, 'installFromInstallDir':True, 'programTarget':False,'libTarget':True,  'publishIncludes':True,  'dependency':True,'Cpack':True, 'Install':True, 'export':True, 'generateStaticLib':True, 'generateSharedLib':True,  'generateJDescription':True},
           'package': {'template':True,'installReleaseInfo':False,'generateVersion':False, 'setenv':False, 'installDependency':True ,'init':True, 'definition':True, 'installFromInstallDir':True, 'programTarget':False,'libTarget':True,  'publishIncludes':True,  'dependency':True,'Cpack':True, 'Install':True, 'export':True, 'generateStaticLib':False,'generateSharedLib':True,  'generateJDescription':False},
           'subdir':  {'template':True,'installReleaseInfo':True,'generateVersion':False, 'setenv':True,  'installDependency':False,'init':True, 'definition':True, 'installFromInstallDir':True, 'programTarget':True, 'libTarget':False, 'publishIncludes':False, 'dependency':True,'Cpack':True, 'Install':True, 'export':False,'generateStaticLib':False,'generateSharedLib':False, 'generateJDescription':False}}
    
    if 'BGEN_RELEASE_PATH' in os.environ:
        rd = ReleaseDrive(os.environ['BGEN_RELEASE_PATH']) 
        verState = rd.checkVersion(path, jdependency[path]["version"], os.environ['BGEN_ARCH'])  
        if verState == VersionStatus.EXIST:
            print("Warning: Version '{}-{}' has been alredy released. Change {} file.".format(path, jdependency[path]["version"], VERSION_FILE_NAME))

        if verState == VersionStatus.OLDER:
            print("Warning: Version '{}-{}' is older then version already released. Change {} file.".format(path, jdependency[path]["version"], VERSION_FILE_NAME))

   
    #print("generate in {}".format(os.getcwd()))
    mode = getComponentType(path)
    
    if mode not in logic:
        print("Error: Wrong directory structure. The mode '{}' is unknown.".format(mode))
        exit(1)
       
    
       
    data={"version":"0.0.0", "companyName":"MYCOMPANY", "derivedTargetsToExport":[]}
    data["buildType"]=buildType
    data["rootPath"] = rootPath
    data["bgen"] = os.path.abspath(__file__).replace("\\", "/") 
    data["mode"] = mode
    data["logic"] = logic[mode]
    data["name"] = os.path.basename(os.getcwd())
    data["path"] = path
    data["generateStaticLib"]          = logic[mode]['generateStaticLib']
    data["generateSharedLib"]          = logic[mode]['generateSharedLib']
    data["export"]                     = logic[mode]['export']
    data["generateJDescription"]       = logic[mode]['generateJDescription']
    data["generateVersion"]            = logic[mode]['generateVersion']
    data['libSpecs']={}
    data['moduleSpecs']=[]
    data['packageSpecs']=[]
    data['systemDepSpecs']=[]
    data['runPath']=runPath
    data['cmakeDefines']={}
    data['cmakeList']={}
    data["installLibPrefix"]="lib"
    data["runTimelibraries"]=[]
    data["libRalation"]={}
    data["version"]    = "0.0.0"
    data["src"] = ["*.cpp","*.c","*.cxx","*.s"]
    data["includeDirs"] = [".", "./public_headers"]    
    data["settings"] = {} 
    data["install"] = []
    data["resource"] = []
    data["installationPrefix"] = getInstallationPrefix(runPath)
    data["test"] = False
    data["jconfig"] = jdependency[path]["jconfig"]
    data["BGEN_ARCH"] = os.environ['BGEN_ARCH']
    data["noLibPrefix"] = False
    data["noLibSuffix"] = False
    data["sourceGeneratedByBgen"]=[]
    data["headerGeneratedByBgen"]=[]
    data["parentSourceGeneratedByBgen"]=[]    
    readRunTimeLibraries(data);
    readSetsFromEnv(data);
    data['targetSystem'] = platform.system().upper()
    data['objBasedHierarchy'] = False

    if 'BGEN_OBJ_BASED_HIERARCHY' in os.environ and os.environ['BGEN_OBJ_BASED_HIERARCHY'] == '1':
        data['objBasedHierarchy'] = True
 
    if mode != 'product' and data['objBasedHierarchy']:
        print("Skipping generation for '{}' since 'objBasedHierarchy' option is enabled".format(data['name']))
        return

    if "CMAKE_SYSTEM_NAME" in data['cmakeDefines']:
        data['targetSystem'] = data['cmakeDefines']['CMAKE_SYSTEM_NAME'].upper()
    
    if data['targetSystem'] == platform.system().upper():
        data['crossComilation'] = False
    else:
        data['crossComilation'] = True

    if data['targetSystem'] == "WINDOWS":        
        if mode not in ["lib"]: # install libraries together with exe. Windows does not have rpath.
            data["installLibPrefix"] = "bin"

    if (logic[mode]['installFromInstallDir']):  
        dlist = getAllDependency(jdependency[path]);
        dlist[path] = {"type":data["mode"],"name":data["name"]}  

        for key, value in dlist.items(): 
            ipath = "{}/{}/{}".format(rootPath,key,"install");
            if (os.path.isdir(ipath)):
                for item in glob.glob(ipath+"/**/*", recursive=True):
                    if os.path.isfile(item):
                        source = item;
                        destination = os.path.dirname(item[len(ipath)+1:])                               
                        source = source.replace("\\", "/")
                        destination = destination.replace("\\", "/")
                        # print("Add to install: {}  -> {}".format(source, destination))
                        data["install"].append({"source":source,"destination":destination})

                
    if (os.path.isfile(VERSION_FILE_NAME)):
        source = os.getcwd()+"/"+VERSION_FILE_NAME
        source = source.replace("\\", "/")
        data["install"].append({"source":source,"destination":"bin"})
   
    if (os.path.isdir("resource")):
        for item in glob.glob("resource/**/*", recursive=True):
            if os.path.isfile(item):
                sitem = "/".join(item.split(os.sep)[1:])
                fileName = os.path.basename(sitem).replace(".","_")
                prefix = path + "_" +str(os.path.dirname(sitem))
                prefix = prefix.replace("/","_")
                prefix = prefix.replace("\\","_")
                
                resPath = os.getcwd()+"/"+item
                resName  = prefix+"_"+fileName                              
                outPath = "generated" + "/" + resName
                resCompiler(resPath, runPath+"/"+path+"/"+"bin"+"/"+outPath)
                resPath = resPath.replace("\\", "/")
                outPath = outPath.replace("\\", "/")              
                data["sourceGeneratedByBgen"].append(outPath+".c") 
                data["resource"].append({"name":resName,"in":resPath, "out":outPath})                                                        
                #data["resource"].append({"name":resName,"path":resPath})
 
    
    if  arguments['--cxxflags']:
        data["cxxflags"] = arguments['--cxxflags']
    
    if  arguments['--cflags']:
        data["cflags"] = arguments['--cflags']

    if  arguments['--lflags']:
        data["lflags"] = arguments['--lflags']
    
  
    data["name"] = nameDecorator(mode, data["name"], path)
  
    #if mode == "subdir":
    #    items = path.split("/")   
    #    data["name"] = "_".join(items[1:])
    #elif mode == "module":
    #    data["name"] = getModuleTargetName(os.path.basename(os.getcwd()))
        
        
        
    data["settings"] = jdependency[path]["settings"]
    if data["settings"]['gtest'] == True:       
        data["test"] = True
        
    if data["settings"]['test'] == True:        
        data["test"] = True
        
    jdependency[path]["sourceGeneratedByBgen"] =  data["sourceGeneratedByBgen"]

    if "parentPath" in  jdependency[path]:    
        data["parentPath"] = jdependency[path]["parentPath"]
        data["parentName"] = jdependency[path]["parentName"]
        data["parentSrc"]=["*.cpp", "*.c", "cxx"]
        data["parentSourceGeneratedByBgen"] =  jdependency[data["parentPath"]]["sourceGeneratedByBgen"]
        if "parentSrc" in jdependency[path]:
            data["parentSrc"] = jdependency[path]["parentSrc"]
        data["parentObjTargetName"] = jdependency[path]["parentName"]
        if jdependency[path]["parentType"] == "module":
            data["parentObjTargetName"] = "object_"+jdependency[path]["parentName"]+"_module"

   
    
        
    #aditionalTargetsToExport=[]
    #treeToinstallDependencies(aditionalTargetsToExport, localTreeDep)     
    #for item in aditionalTargetsToExport:       
       # data["derivedTargetsToExport"].append("static_"+item['node']['spec']['name'])
       #todo review code coresponding to derivedTargetsToExport

    if (logic[mode]['libTarget'] and logic[mode]['Cpack']) :
        data["installIncludes"] = True 
    else:
        data["installIncludes"] = False  
    #if ('src' in jdependency[path]):        
    #   data['src'] = data['src'] #+ jdependency[path]['src']
        
    
    data["templates"]=[]
    data["version"] = jdependency[path]["version"]  

    data["installationSuffix"] = jdependency[path]["installationSuffix"]  

    if (logic[mode]['dependency']):
        dependency = jdependency[path]["dependency"]
 
            
        for d in dependency:                        
            if ("platforms" in d) and data["BGEN_ARCH"] not in d["platforms"]:
                if VERBOSE:
                    print("Ignoring dependency {}/{}. because derived dependency use difrent platform {}".format(d["type"],d["name"], d["platforms"]))

                continue
            if ("visibility" not in d) or (len(d["visibility"]) == 0):
                d["visibility"]="private";
            if (d["type"] == "package"):
                addPckageDependency(d, data['packageSpecs']);         
            elif (d["type"] == "module"):
                addSubModuleDependency(d, data['moduleSpecs'])
            elif (d["type"] == "system"):
                addSystemDependency(d, data['systemDepSpecs'])
            elif (d["type"] == "3rdParty"):
                addLibDependency(d,"3rdParty", data['libSpecs'],buildType, skipstaticinstall);
            elif (d["type"] == "lib"):    
                addLibDependency(d, "lib", data['libSpecs'],buildType, skipstaticinstall);           
            else:
                print ("Error: unknown type of dependency: '{}'".format(d["type"] ))
                exit(1)      
        for pathd in jdependency[path]["derived_dependency"]:
            d = jdependency[path]["derived_dependency"][pathd]
            if ("visibility" not in d) or (len(d["visibility"]) == 0):
                d["visibility"]="private";
            if ("platforms" in d) and data["BGEN_ARCH"] not in d["platforms"]:
                if VERBOSE:
                    print("Ignoring derived dependency {}/{}. because derived dependency use difrent platform {}".format(d["type"],d["name"], d["platforms"]))
                continue
            if (d["type"] == "lib"):
                addLibDependency(d, "lib", data['libSpecs'],buildType, skipstaticinstall);   
            if (d["type"] == "3rdParty"):
                addLibDependency(d,"3rdParty", data['libSpecs'],buildType, skipstaticinstall);  
            elif (d["type"] == "system"):
                addSystemDependency(d, data['systemDepSpecs'])
            
            
    agregat=[]
    if (logic[mode]['init']):
        agregat.append({"t":Template(cmakeInit.__doc__),"data":data,"name":"cmakeInit"})
    
    if (logic[mode]['template']):
        templatePaths = glob.glob("./template/*", recursive=False)
        if len(templatePaths) != 0:
            for item in templatePaths:
                if os.path.isdir(item):
                    templateFiles = glob.glob(item+"/*.thpp", recursive=False)
                    #data["templates"].append({"dir":os.path.basename(item),"template":os.path.basename(item)+".thpp"})
                    #print("templateFiles:",templateFiles,item+"/*.thpp")
                    for templateitem in templateFiles:
                       data["templates"].append({"dir":os.path.basename(item),"template":os.path.basename(templateitem)})
                #print(data["templates"])
            agregat.append({"t":Template(cmakeTemplate.__doc__),"data":data,"name":"cmakeTemplate"})
    
    
    if (logic[mode]['generateVersion']):
        data["sourceGeneratedByBgen"].append("generated/" + data['name'] + "/version.c")
        data["headerGeneratedByBgen"].append("generated/" + data['name'] + "/version.h")
        agregat.append({"t":Template(VersionGenerator.__doc__),"data":data,"name":"VersionGenerator"})
        
    
    if (logic[mode]['definition']):                
        agregat.append({"t":Template(cmakeDefinitions.__doc__),"data":data,"name":"cmakeDefinitions"})


    if len(data["resource"]) > 0:
        agregat.append({"t":Template(ResourceGenerator.__doc__), "data":data,"name":"ResourceGenerator"})
#kuku to remove
#        processTempates(runPath+"/"+path+"/"+"bin"+"/"+"generated"+"/"+data['name']+"/"+"resource.h", [{"t":Template(HeaderResource.__doc__), "data":data,"name":"HeaderResource"}])


    generateUserIncludes(agregat, data, rootPath, "BGEN INCLUDE", True)
    generateUserIncludes(agregat, data, './', "USER INCLUDE", True)
    for lib in data['libSpecs']:
        generateUserIncludes(agregat, data, data['libSpecs'][lib]['path']+"/bgen/", "LIB INCLUDE", True)
 
    if data['objBasedHierarchy']:
        for spec in data['moduleSpecs']:
            generateUserIncludes(agregat, data, '../../module/{}'.format(spec['name']), "USER INCLUDE({})".format(spec['name']), True)

 
     
    agregat.append({"t":Template(cmakeSubmodule.__doc__), "data":data,"name":"cmakeSubmodule"})          

    data["exetargetName"] = data["name"]
    if ("targetName" in data['settings']):
        data["exetargetName"] = data['settings']['targetName']

    data["mainTargets"] = [data["exetargetName"]]

    if "BGEN_SET_CMAKE_SYSTEM_NAME" in os.environ:
        ENV_SYSTEM_NAME= os.environ['BGEN_SET_CMAKE_SYSTEM_NAME'].upper()
    else:
        ENV_SYSTEM_NAME= ""

    if platform.system().upper() == "WINDOWS" and buildType == "Debug" and ENV_SYSTEM_NAME != "GENERIC" and ENV_SYSTEM_NAME != '"GENERIC"':
        source = "{}/{}/bin/Debug/{}".format(runPath, path, data["exetargetName"]+".pdb")
        source = source.replace("\\", "/")
        data["install"].append({"source":source,"destination":"bin"})


    data["intermediateTargets"] = []        
    if (logic[mode]['programTarget']):
        agregat.append({"t":Template(cmakeAddProgramTarget.__doc__), "data":data,"name":"cmakeAddProgramTarget"})
   
    if (logic[mode]['installFromInstallDir']):
        agregat.append({"t":Template(cmakeInstallFromInstallDir.__doc__), "data":data,"name":"cmakeInstallFromInstallDir"})

 
        
    if (logic[mode]['libTarget']):
        data["mainTargets"] = []
        if data["generateStaticLib"]:
            data["otargetName"] = "static_"+data["name"]
            if ("targetName" in data['settings']) and not data["generateSharedLib"]:
                data["otargetName"] = data['settings']['targetName']
            data["mainTargets"].append(data["otargetName"])
        if data["generateSharedLib"]:
            data["sotargetName"] = "shared_"+data["name"]
            if ("targetName" in data['settings']):
                data["sotargetName"] = data['settings']['targetName']
                data["noLibPrefix"] = True
                ext = os.path.splitext( data['settings']["targetName"])[-1]
                if ext != "":
                    data["noLibSuffix"] = True

            data["mainTargets"].append(data["sotargetName"])

        data["intermediateTargets"] = ["object_"+data["name"]]
        agregat.append({"t":Template(cmakeLibTarget.__doc__), "data":data,"name":"cmakeLibTarget"})                       
    if (logic[mode]['publishIncludes']):        
        agregat.append({"t":Template(cmakePublishIncludes.__doc__), "data":data,"name":"cmakePublishIncludes"})
        
        
    agregat.append({"t":Template(cmakeSubmoduleDependency.__doc__), "data":data,"name":"cmakeSubmoduleDependency"})              
    agregat.append({"t":Template(cmake3rdParty.__doc__), "data":data,"name":"cmake3rdParty"})
    agregat.append({"t":Template(cmakePackageDependency.__doc__), "data":data,"name":"cmakePackageDependency"}) # not used
    agregat.append({"t":Template(cmakeSystemDependency.__doc__), "data":data,"name":"cmakeSystemDependency"}) 
       
    if (logic[mode]['Cpack']):        
        agregat.append({"t":Template(cmakeCPack.__doc__), "data":data,"name":"cmakeCPack"})
    
    if (logic[mode]['Install']): # and data['targetSystem'] != "WINDOWS":   
        agregat.append({"t":Template(cmakeInstall.__doc__), "data":data,"name":"cmakeInstall"})
        #if (logic[mode]['programTarget']):
        #    agregat.append({"t":Template(cmakeSHscript.__doc__), "data":data,"name":"cmakeSHscript"})
                        
    if (logic[mode]['generateJDescription']):
        if data['targetSystem'] == "WINDOWS":
            data['libRalation']={data["sotargetName"]:[".."+"/"+"bin"+"/"+"lib"+data["sotargetName"]+".dll"]}
        agregat.append({"t":Template(jsonLibDescription.__doc__), "data":data,"name":"jsonLibDescription"})
        # dont use Template in the future. USe generateJsonLibDescription function
        # generateJsonLibDescription(jdependency, path, data)
   
    
    if logic[mode]['installDependency'] or (logic[mode]['Install'] ):
        agregat.append({"t":Template(cmakeInstallDependency.__doc__), "data":data,"name":"cmakeInstallDependency"})   
    
    generateUserIncludes(agregat, data, rootPath, "BGEN INCLUDE", False)         
    generateUserIncludes(agregat, data, './', "USER INCLUDE", False)
    for lib in data['libSpecs']:
        generateUserIncludes(agregat, data, data['libSpecs'][lib]['path']+"/bgen/", "LIB INCLUDE", False)
 
    if data['objBasedHierarchy']:
        for spec in data['moduleSpecs']:
            generateUserIncludes(agregat, data, '../../module/{}'.format(spec['name']), "USER INCLUDE({})".format(spec['name']), False)

        
    if (logic[mode]['export']):
        processTempates(data['name']+"Config.cmake", [{"t":Template(cmakeConfigFile.__doc__), "data":data,"name":"cmakeConfigFile"}])
    
        
    if (logic[mode]['installReleaseInfo']):
        agregat.append({"t":Template(InstallReleaseInfo.__doc__),"data":data,"name":"InstallReleaseInfo"})
        
        if 'rd' in locals():
            if rd.isVersionExist(data["path"], data["BGEN_ARCH"], data["version"]):
                if (skipVersionCheck):
                    print("Warning: the version '{}' for '{}' already exist on the release drive.".format(data["version"], data["path"]))
                else:
                    print("Error: the version '{}' for '{}' already exist on the release drive.".format(data["version"], data["path"]))
                    exit(1)
    
    processTempates("src/CMakeLists.txt", agregat)
    jdependency[path]["data"] = data
    
    x = json.JSONEncoder(indent=4).encode(jdependency[path])     
    
       
    writeFileIfChanged(data["runPath"]+"/bgen/generated/"+path+"/"+data["exetargetName"]+"_releaseInfo.json", x)
  
        
    
        
def getComponentPath(path):
    items = path.split("/")
    if len(items) < 2:
        print("Error: wrong directory depth. {}".format(path))
        exit(1)
    return items[len(items)-2] + "/" + items[len(items)-1]
    
def getComponentType(path):
    items = path.split("/")    
    if (len(items) == 4):
        return "subdir"
    if (len(items) == 2):
        return items[len(items)-2]
        
    print("Error: wrong path size. {}".format(path))
    exit(1)


def treeToinstallDependencies(flatList, parent):    
    for item in parent['children']:        
        if (item['spec']['type'] in ['module'] and (item['spec']['visibility'] == 'public' or parent['spec']['type'] in ['module'])) :
            flatList.append({'path':item['spec']['type'] + "/" +item['spec']['name'],'node':item})
            treeToinstallDependencies(flatList,item)

def isFileInGit(path):
    try:
        value = subprocess.check_output(["git", "cat-file", "-e", "HEAD:"+path], stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        return False
    return True

def isFileInGitOLD_TO_REMOVE(path):
    try:
        value = subprocess.check_output(["git", "ls-files", path], stderr=subprocess.STDOUT)  
    except subprocess.CalledProcessError as e:
        return False
    
    if len(value)==0:
        return False
    return True

def readFileFromGit(path):
    if (os.path.isfile(path)):
        try: 
            f = open(path, 'r')
            retValue = f.read()               
            f.close()
        except IOError:
            print ("Error: Could not read file: '{}'".format(path))
            exit(1)
        return retValue
    command ="git show HEAD:"+path
    return shellCmd(command)
   
def findMain(path):
    for mainFile in glob.glob(path + "/" + "src" + "/" + "main.cpp"):
        mainFile = mainFile.split("/")
        return "/".join(mainFile[2:])


def updateDefaultSettings(pathToFile, cfg, arch):
    allowedSettings=['targetName','std','cxxflags','cflags', 'lflags', 'gtest', 'test' ]
    if "settings" not in cfg:
        cfg["settings"]={}

    platformSpecificSettings = {}
    
    if "platforms" in cfg and type(cfg["platforms"]) is dict and arch in cfg["platforms"]:
        platformSpecificSettings = cfg["platforms"][arch]
           
    if "settings" in platformSpecificSettings:
        for item in platformSpecificSettings["settings"]:
            cfg["settings"][item] = platformSpecificSettings["settings"][item]

    for item in cfg["settings"]:
        if item not in allowedSettings:
            print("Error: unknown settings '{}' in '{}'. Allowed settings are: {}".format(item, pathToFile, ", ".join(allowedSettings)))
            exit(1)
    
    if "std" not in cfg["settings"]:
        cfg["settings"]["std"] = ""

    if "cxxflags" not in cfg["settings"]:
        cfg["settings"]["cxxflags"] = ""
    
    if "cflags" not in cfg["settings"]:
        cfg["settings"]["cflags"] = ""  

    if "lflags" not in cfg["settings"]:
        cfg["settings"]["lflags"] = ""                          
    
    if "gtest" not in cfg["settings"]:
        cfg["settings"]["gtest"] = False
        
    if "test" not in cfg["settings"]:
        cfg["settings"]["test"] = False     


  
def doesLibIsInDependency(d, dependencyType, libName):
    found = False
    for item in d:
        if item["name"] == libName and item["type"] == dependencyType:
            found = True
            break
    return found
    
def addDependencyIfNotExist(d,lib):
    found = doesLibIsInDependency(d, "3rdParty", lib["name"])
    
    if found == False:
        d.append(lib)

def addDefaultSubfolders(name,skipSubFolders, jdependency, path):
    if os.path.isdir(path+"/"+name) and not skipSubFolders:
        searchPath = path+"/"+name
        for gtestName in os.listdir(searchPath):
            if not os.path.isdir(searchPath+"/"+gtestName):
                continue
            gtestPath = name+"/"+gtestName
            dependencyPath = searchPath+"/"+gtestName +"/dependency.json"
            
            
            if not "subfolders" in jdependency[path]:
                jdependency[path]['subfolders'] = {}
            if os.path.isfile(dependencyPath):
                defaultGtestSettings = readFile(dependencyPath)
                if gtestPath in jdependency[path]['subfolders']: 
                    print("Error: You cannot define two dependendy for one subfolder.")
                    print("   One dependency found in: '{}'".format(dependencyPath))
                    print("   Second found INSIDE this file: {}".format(path+"/dependency.json"))
                    exit(1)
                
            else:
                if not gtestPath in jdependency[path]['subfolders']:               
                    defaultGtestSettings = {
                        "parentSrc":["*.cxx","*.cpp", "*.c"],
                        "dependency":[],
                     "settings":{
                     }
                     }
                
                    defaultGtestSettings['dependency'] = copy.deepcopy(jdependency[path]["dependency"])
                    for item in defaultGtestSettings['dependency']:
                        if "version" in item:
                            item.pop("version")
            jdependency[path]['subfolders'][gtestPath] = defaultGtestSettings
            if (name == "gtest"):
                if "settings" not in jdependency[path]['subfolders'][gtestPath]:
                    jdependency[path]['subfolders'][gtestPath]["settings"] = {}
                jdependency[path]['subfolders'][gtestPath]["settings"]["test"] = True


def dependencyReader2(path, jdependency, arch, skipSubFolders=False, exclude=['system','3rdParty','lib']):
    pathToCfg = path + "/" + DEPENDENCY_FILE_NAME
    pathToVer = path + "/" + VERSION_FILE_NAME
   
    if path in jdependency :
        return True 
    try: 
        fcontent = readFileFromGit(pathToCfg)        
        cfg = json.loads(fcontent) 
        if ("paltforms") in cfg:        
            print("Error: the 'paltforms' property in the '{}' asset is obsolete. Please change it into 'platforms' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.".format(path)) 
            if ("platforms" not in cfg):
               cfg['platforms'] = cfg['paltforms']


        if ("platforms" not in cfg):
            print('Error[1]: unknown supported platform for {}. Please add \'"platforms":["<platform name>"]\' to the dependency file. '.format(path))
            exit(1)


        if arch != "ALL" and arch not in (cfg["platforms"]):
            print("Info: skipping '{}' because unsuported platform '{}'.".format(path,arch));
            return False
        

        cfg["version"] = versionReader(pathToVer)["version"]            
        cfg["installationSuffix"] = getInstallationSuffix(path,cfg["version"] ,arch, False)
        
        
        
        updateDefaultSettings(pathToCfg, cfg, arch)
        jdependency[path] = cfg
    except IOError:
        print ("Error: Could not read file:", pathToCfg)
        exit(1)
    except ValueError as e:
        print ("Error: wrong json format in file: '{}'".format(pathToCfg))
        print(e)
        exit(1)
    
    if 'main' not in jdependency[path]:
        jdependency[path]['main'] = findMain(path)
    jdependency[path]["dependencyPaths"] = {}


    for d in jdependency[path]["dependency"]:
        # add platforms if not exist
        if "platforms" not in d:
            d["platforms"]=[os.environ['BGEN_ARCH']]
        if os.environ['BGEN_ARCH'] not in d["platforms"]:
            continue
 
        newPath = d['type'] + "/" +d['name']       
        if newPath in jdependency[path]["dependencyPaths"]:
            print("Error: the component '{}' has two dependency for '{}' in the dependency.json file. Try use the different platform for bought entries".format(path, newPath));
            exit(1)
        jdependency[path]["dependencyPaths"][newPath] = d
        if d['type'] not in exclude:     
            if dependencyReader2(newPath,jdependency, arch, skipSubFolders,  exclude) == False:
                print("Skip '{}' folder becausse missing dependency: '{}'".format(path,newPath))  
                return False

    #skipSubFolders = False
    addDefaultSubfolders("gtest", skipSubFolders, jdependency, path)
    addDefaultSubfolders("utility", skipSubFolders, jdependency, path)

    if ("subfolders" in jdependency[path]) and not skipSubFolders:   
        if 'BGEN_RELEASE_PATH' in os.environ:                        
            rd = ReleaseDrive(os.environ['BGEN_RELEASE_PATH'])
  
        for subfolder in jdependency[path]["subfolders"]:
            if "platforms" in jdependency[path]["subfolders"][subfolder]:
                if arch not in jdependency[path]["subfolders"][subfolder]["platforms"]:
                    print("Info: subfolder '{}' ignored because unsupported platofm: {}".format(subfolder, arch));    
                    continue                             
                       
            subPath = path+"/"+subfolder
            mode = getComponentType(subfolder)
            test = False
            gtest = False

            #if not os.path.isdir(subPath):
            #    print("Error: path '{}' does not exist. Check the '{}' dependency file.".format(subPath, pathToCfg))
            #    exit(1)
                                   
            newSettings = {}
            if "settings" in jdependency[path]:
                newSettings = copy.deepcopy(jdependency[path]["settings"])                
            if "settings" in jdependency[path]["subfolders"][subfolder]:
                for s in jdependency[path]["subfolders"][subfolder]["settings"]:                    
                    newSettings[s] = jdependency[path]["subfolders"][subfolder]["settings"][s]
            jdependency[path]["subfolders"][subfolder]["settings"] = newSettings
            
            
            updateDefaultSettings(pathToCfg,jdependency[path]["subfolders"][subfolder], arch)            
            settings = jdependency[path]["subfolders"][subfolder]["settings"]


            if (mode == "gtest") or settings["gtest"] or settings["test"]:
                test = True

            if (mode == "gtest"):
                gtest = True

            if gtest:
                if not doesLibIsInDependency(jdependency[path]["subfolders"][subfolder]['dependency'], "3rdParty", "gtest"):
                # old behavior - automatycally add gtest lib
                    print("Info: see that your gtest ({}) does not have the gtest in the library. Let me find do it for you...".format(path+"/"+subfolder))
                    if 'rd' in locals():
                        gtestVersions = rd.getVersions("3rdParty/gtest", os.environ['BGEN_ARCH'])
                    
                        if len(gtestVersions) == 0:
                            print("Error: Unable to find gtest library in 3rdParty/gtest.")
                            exit(1)
                        latestGtestVersion = gtestVersions[-1]
                    if platform.system().upper() != "WINDOWS":
                        addDependencyIfNotExist(jdependency[path]["subfolders"][subfolder]['dependency'],
                        {"type":"system",  "name": "pthread","visibility":"private", "platforms":[arch]})
                    addDependencyIfNotExist(jdependency[path]["subfolders"][subfolder]['dependency'],
                        {"type":"3rdParty",  "name": "gtest", "link_libraries":["gmock","gtest_main", "gtest"],"visibility":"private", "version":latestGtestVersion})


                      
            #test = settings["gtest"] or settings["test"]
                
            jdependency[subPath] = jdependency[path]["subfolders"][subfolder]
            jdependency[subPath]["version"] = jdependency[path]["version"]
            jdependency[subPath]["installationSuffix"] = getInstallationSuffix(path,cfg["version"] ,arch, test)
            jdependency[subPath]["parentPath"] = path
            jdependency[subPath]["parentName"] = os.path.basename(path)
            jdependency[subPath]["parentType"] = os.path.dirname(path)
            jdependency[subPath]["dependencyPaths"]={}            
            

            for d in jdependency[subPath]["dependency"]:
                newPath = d['type'] + "/" +d['name']       
                jdependency[subPath]["dependencyPaths"][newPath] = d                            
        
                if newPath in jdependency[path]["dependencyPaths"]:                    
                    if 'version' in jdependency[path]["dependencyPaths"][newPath]:                       
                        if 'version' in d:                        
                            print("Error: in '{}' file. Do not specify version in the dependency for the subfolders. '{}' , '{}' if used by parent.".format( pathToCfg, subPath , newPath))
                            exit(1)
                        else:
                            # copy version from main target                            
                            d['version'] = jdependency[path]["dependencyPaths"][newPath]['version']                                                    
                if d['type'] not in exclude:                                                        
                    if dependencyReader2(newPath,jdependency, arch, skipSubFolders, exclude) == False:
                        print("Skip '{}' folder becausse missing dependency: '{}'".format(path,newPath))
                        return False

    
    return True           
    #TODO: check reclusive loop (if one of the item already exist in the back probe to root than give error)
    
def checkEnvSettings():
    if "BGEN_ARCH" not in os.environ:
        print ("Error: the BGEN_ARCH environment variable is not defined.")
        exit (1)

def IsCmakeHardcoded(componnetPath):
    return (os.path.isfile(str(componnetPath)+"/"+"src"+"/"+"CMakeLists.txt")) and isFileInGit(str(componnetPath)+"/"+"src"+"/"+"CMakeLists.txt")

def IsComponenetLevel(componnetPath):
    fileSymptoms =  os.path.isfile(componnetPath+"/"+DEPENDENCY_FILE_NAME) and os.path.isdir(componnetPath+"/"+"src") 
    
    if not fileSymptoms:  
        if IsCmakeHardcoded(componnetPath):#for QT project where cmake is hardcoded
            fileSymptoms = True
 
    pathStructureSymptom = False;
    items = componnetPath.split("/")
    if (len(items) < 2):
        return False;
    
    if (items[len(items)-2]) in ['product','module','lib']:
        pathStructureSymptom = True
    return (pathStructureSymptom and fileSymptoms)
    
def IsFinalComponenetLevel(componnetPath):
    if IsComponenetLevel(componnetPath):        
        items = componnetPath.split("/")        
        if (len(items) < 2):
            return False;
        if (items[len(items)-2]) in ['product','lib']:
            return True
        return False

def IsModuleComponenetLevel(componnetPath):
    if IsComponenetLevel(componnetPath):
        items = componnetPath.split("/")
        if (len(items) < 2):
            return False;
        if (items[len(items)-2]) in ['module']:
            return True
        return False
  
def IsRootLevel(componnetPath):
    tryPath = componnetPath+"/"+"product"
    if os.path.isdir(tryPath) and IsComponentsContainerLevel(tryPath):
        return True

    tryPath = componnetPath+"/"+"module"
    if os.path.isdir(tryPath) and IsComponentsContainerLevel(tryPath):
        return True    

    tryPath = componnetPath+"/"+"lib"
    if os.path.isdir(tryPath) and IsComponentsContainerLevel(tryPath):
        return True
    
    return False

def IsGitRepo(componnetPath):
    items = componnetPath.split("/")
    if os.path.isdir(componnetPath+"/.git"):
        return True
    if (len(items) < 2):
        return False;
    zc = 0;
    for i in items:
        if len(i) == 0: 
            zc =  zc + 1
    if zc == len(items):
        return False
    
    return IsGitRepo(os.path.dirname(componnetPath))
    
    
    
def IsComponentsContainerLevel(componnetPath):
    items = componnetPath.split("/")
    if (len(items) < 2):
        return False;
   
    if (os.path.basename(componnetPath) not in ['product','module','lib']):
        return False;
   
    
    for path in os.listdir(componnetPath):
        path = componnetPath + "/" +path
        if not os.path.isdir(path):
            continue
        if IsComponenetLevel(path):
            return True;
                        
    return False
    
    
def detectLevel(targetPath):
    if (IsFinalComponenetLevel(targetPath)):
        return "FINAL_COMPONNENT"    
    elif (IsModuleComponenetLevel(targetPath)):
        return "MODULE_COMPONNENT"        
    elif (IsComponentsContainerLevel(targetPath)):
        return "CONTAINER"        
    elif (IsRootLevel(targetPath)):
        return "ROOT"        
    
    return None
    
def cwdRoot():
    maxDepth=5
    while not IsRootLevel(os.getcwd()) and maxDepth > 0:
        os.chdir("../")
        maxDepth = maxDepth - 1
           
    if(maxDepth == 0):
        print("Error: This directory is not a bgen repo structure.")
        exit(1)


def collectChangedComponnents(components, sha):    
    filesName = shellCmd("git diff --name-only "+sha)
    #print(filesName)
    detectedChanges = []
    for fileName in filesName.split():
        fileNameArr = fileName.split("/")
        if (len(fileNameArr) > 2):
            if fileNameArr[0] in SUPPORTED_COMPONENTS:
                componnetToAdd = fileNameArr[0]+"/"+fileNameArr[1]
                detectedChanges.append(componnetToAdd)
    for item in list(dict.fromkeys(detectedChanges)):
        assetStatus = shellCmd("git show HEAD:"+item+"/"+DEPENDENCY_FILE_NAME, True)
        if (assetStatus == None):
            modifyer = " - ignored, unable to find on disk."
        else:
            modifyer = ""
            components.append(item)
        print("Detected change in '{}' asset{}".format(item,modifyer))
        
    
def readAllDependency():
    print("Reading all dependency...")
    cwdRoot()
    assetPaths=[]
    #TODO: use getAssetList to get asset list 
    proc = subprocess.Popen(['git', 'ls-tree', '-r', 'HEAD', '--name-only'],stdout=subprocess.PIPE)
    while True:
        line = proc.stdout.readline().decode("utf-8").rstrip()
        
        if not line:
            break
        if os.path.basename(line) == DEPENDENCY_FILE_NAME:
            items = line.rstrip().split("/")
            if len(items) != 3:
                continue
            if items[0] not in SUPPORTED_COMPONENTS:
                continue
            assetPaths.append(items[0]+"/"+items[1])
            

 #   for assetType in SUPPORTED_COMPONENTS:
 #       if os.path.isdir(assetType):
 #           for item in os.listdir(assetType):
 #               dirName = assetType + "/" + item
 #               if os.path.isdir(dirName):
 #                   #print(assetType +"/"+ item)
 #                   #print(dirName+"/"+DEPENDENCY_FILE_NAME)
 #                   if (os.path.isfile(dirName+"/"+DEPENDENCY_FILE_NAME)):
 #                       assetPaths.append(assetType +"/"+ item)
                
   
    jdependency={}
    for item  in assetPaths:
        dependencyReader2(item, jdependency,"ALL",skipSubFolders = False)
        #print(item)
    return jdependency
    
def getParentsOf(path,parents, jdependency, exclude=['system','3rdParty','lib'],lastParent=['system','3rdParty','lib','program']):
    if  'parents' in jdependency[path]:
        parents=jdependency[path]['parents']
        return 
    jdependency[path]['parents']=[]
    for parent in jdependency:
        for childItem in jdependency[parent]['dependency']:
            if childItem['type'] not in exclude: 
                child = childItem['type'] + "/" +childItem['name'] 
                if(child == path):
                    if parent not in jdependency[path]['parents']:
                        jdependency[path]['parents'].append(parent)
                        grandparents=[]
                        parentType = getComponentType(parent)
                        if parentType in lastParent:
                            continue
                        getParentsOf(parent,grandparents, jdependency, exclude, lastParent)
                        for grand in grandparents:
                            if grand not in jdependency[path]['parents']:
                               jdependency[path]['parents'].append(grand)
    
    for parent in jdependency[path]['parents']:
        if parent not in parents:
            parents.append(parent)
    return
    
    
def getChildrenOf(path,children, jdependency, exclude=['system','3rdParty','lib']):
    if  'children' in jdependency[path]:
        children=jdependency[path]['children']
        return 
    jdependency[path]['children']=[]
    itemToProcess = jdependency[path]["dependency"];
    if "subfolders" in  jdependency[path]:
        for subfolder in  jdependency[path]["subfolders"]:
            itemToProcess = itemToProcess + jdependency[path]["subfolders"][subfolder]["dependency"] 
    for d in itemToProcess:
        if d['type'] not in exclude: 
            child = d['type'] + "/" +d['name']  
            if child not in jdependency[path]['children']:
                jdependency[path]['children'].append(child)
                subChild=[]  
                getChildrenOf(child,subChild,jdependency,exclude)
                for sub in subChild:
                    if sub not in jdependency[path]['children']:
                        jdependency[path]['children'].append(sub)
             
    for child in jdependency[path]['children']:
        if child not in children:
            children.append(child)
                
def striptPath(path):    
    line = re.sub('^\./','',path)
    line = re.sub('/$','',line)
    return line

def hardRemove(level, whiteList):
    if not os.path.isdir(level):
        return
    for item in os.listdir(level):

        path = level + "/" + item
        if os.path.isdir(path) and (path not in whiteList):
            print("removing {}...".format(path))
            shutil.rmtree(path)


def printInColumn(myList, col):
#    maxwidth = max(map(lambda x: len(x), mylist)) 
#    TODO: print in collumn
    for item in myList:
        print(item)

def getAssetList():
    past = ["product/"+ sub for sub in  shellCmd("git show HEAD:product").splitlines()]  
    past = past +  ["module/" + sub for sub in shellCmd("git show HEAD:module").splitlines()]
    past = past +  ["lib/" + sub for sub in shellCmd("git show HEAD:lib").splitlines()]
    assets = []
    for path in past:
        if isFileInGit(path+DEPENDENCY_FILE_NAME):
            assets.append(path)

    return assets

#***********************************************************************
#*                       SHOW                                          *
#***********************************************************************
def getCurrentShow():
    components=[]
    with open (".git/info/sparse-checkout", "r") as fileHandler:
        for line in fileHandler:
            sline = line.strip("\n\r").split("/");
            if (len(sline) == 3):
                if (sline[1] in ["module","product","lib"]):                       
                    component = "/".join(sline[1:])
                    components.append(component)
        return components
                
def startShow(components, showAll, showChanged, sha, parent, hard, lst, refresh, add, remove):
    components = list(map(striptPath, components)) 
    cwdRoot() 
    if (not os.path.isdir(".git")):
        print("Error: this is not a git repozytory.")
        exit(1)
         
    if (remove): 
        oldSetup = getCurrentShow()
        for item in components:           
            if item in oldSetup:
                oldSetup.remove(item)               
        components = oldSetup
    
    if (add): 
        oldSetup = getCurrentShow()   
        for item in oldSetup:
            if item not in components:
                components.append(item)
    
    if (refresh): 
        components=getCurrentShow()           
   
    if lst:
        for item in getAssetList():
            print(item)
        print("\nCurrently showed:")
        for item in getCurrentShow():
            print("    ",item)
              
        return
 
    if len(components) == 0 and (showAll == False):
        print("Error: you cannot remove all assets from show");
        
    jdependency={}
    
    #if showAll == False: - requested by T.medenecki : do not clear my changes whan i do showll all 
    gitStatus = subprocess.check_output(["git", "status", "-uno", "-s"])
    if (len(gitStatus)):
        print("Error: You have untracked or uncommited changes in your repo. ")
        print("Please commit them before run this command:")
        print(gitStatus)
        exit(1)
        
    if parent:
        jdependency = readAllDependency()
        
    if (showChanged):
        collectChangedComponnents(components, sha)
    elif (showAll):
        print("Enable all")
        with open (".git/info/sparse-checkout", "w") as fileHandler:
            fileHandler.write("*\n")
        print("reading tree...")
        subprocess.call(["git", "read-tree", "--reset", "-u", "HEAD"]) 
        return
    
    componentsToCheckout=[]
    if (len(jdependency) == 0):
        print("reading dependencies...")
        for item in components:
            dependencyReader2(item, jdependency,"ALL", skipSubFolders=False)
    
    for item in components:
        componentsToCheckout.append(item)
        getChildrenOf(item,componentsToCheckout, jdependency)
    if parent:
        print("Analyzing parent dependnecies...")   
        for item in components:      
            getParentsOf(item, componentsToCheckout, jdependency, ['system','3rdParty'])
    subprocess.call(["git", "config", "core.sparsecheckout", "true"])        
    with open (".git/info/sparse-checkout", "w") as fileHandler:
        for item in componentsToCheckout:
            fileHandler.write("/"+item+"\n")
            print("add '"+item+"'") 
        for item in os.listdir("./"):
            if (item not in ["module", "lib", "product", ".git" ]):
                print("add none asset '"+item+"'")
                fileHandler.write("/"+item+"\n")

    print("reading tree...")
    subprocess.call(["git", "read-tree", "--reset", "-u", "HEAD"]) 
    if hard:
        hardRemove("module", componentsToCheckout)
        hardRemove("lib", componentsToCheckout)
        hardRemove("product", componentsToCheckout)

    return
    
def generateRegenerateStartingPoint(level, targetPath):    
    regenerateList = [];                           
        
    if (level == "FINAL_COMPONNENT") or (level == "MODULE_COMPONNENT"):
        regenerateList.append(targetPath)
    if (level == "MODULE_COMPONNENT"):
        regenerateList.append(targetPath)
    if (level == "CONTAINER"):
        for item in os.listdir(targetPath):
            if (IsFinalComponenetLevel(targetPath+"/"+item)):
                regenerateList.append(targetPath+"/"+item)
    if (level == "ROOT"):
        for container in os.listdir(targetPath):
                
            if (IsComponentsContainerLevel(targetPath+"/"+container)):
                for item in os.listdir(targetPath+"/"+container):
                    cpath = targetPath+"/"+container+"/"+item
#                    if (IsModuleComponenetLevel(cpath) or IsFinalComponenetLevel(cpath)):
                    if IsFinalComponenetLevel(cpath):
                        regenerateList.append(targetPath+"/"+container+"/"+item)
                    
    return regenerateList

    
def derivedDependencyReader(jdependency, startingPoint,space=""):
    
    if "derived_dependency" in jdependency[startingPoint]:
        return jdependency[startingPoint]["derived_dependency"]
    
    print("{}Collecting dependency {}...".format(space,startingPoint))
    
    derived_dependency={}
    for point in jdependency[startingPoint]["dependencyPaths"]:
        if point in jdependency:             
            derived_dependency.update(jdependency[point]["dependencyPaths"])
            if VERBOSE:
                for items in jdependency[point]["dependencyPaths"]:
                    dep = jdependency[point]["dependencyPaths"][items]
                    platforms = "?"
                    if "platforms" in dep:
                        platforms = dep["platforms"]

                    print(" +{}Add {} {} got from {}".format(space, dep["name"], platforms, point)); 
            derived_dependency.update(derivedDependencyReader(jdependency, point,space+"   "))
    
    jdependency[startingPoint]["derived_dependency"] = derived_dependency
    return derived_dependency
    
def getSkipTrigers(jconfig):
    ret = []
    for option in jconfig["options"]:
        if os.environ['BGEN_ARCH'] in jconfig["options"][option]:
            if "skip" in jconfig["options"][option][os.environ['BGEN_ARCH']]:
                if jconfig["options"][option][os.environ['BGEN_ARCH']]["skip"]:
                    ret.append(option)
    return ret
    
        
#***********************************************************************
#*                       CMAKE GENERATION                              *
#***********************************************************************
def startCmakeGen(buildType, skipstaticinstall):    
    targetPath = arguments['<path>']
    skipVersionCheck = True

    runPath = os.getcwd()
    runPath = runPath.replace("\\", "/")
    
    os.chdir(targetPath)
    cwdRoot() 
    rootPath = os.getcwd()
    jrootConfig = loadConfig(os.path.dirname(os.path.realpath(__file__)))

    if not arguments['--force'] and IsCmakeHardcoded("./"):
        print("Nothink to do. This module has Makefile commited into Git repozytory.")
        exit(0)
             
    if targetPath == None or (len(targetPath) == 0):
        targetPath = '.'+"/"    
    
    if os.path.isabs(str(targetPath)):
        targetPath = targetPath
    else:        
        targetPath = os.path.abspath(runPath+"/"+str(targetPath)) 
   
    targetPath = targetPath.replace("\\", "/")
    print(targetPath)
    level = detectLevel(targetPath)
    
    
    if (level == None):
        print("Error: Wrong target path '{}'.".format(targetPath))
        exit(1)
    regenerateSP = generateRegenerateStartingPoint(level, targetPath)
    
    
    #regenerateList = {};
    jdependency={}
    
    for startingPoint in regenerateSP:        
        print("Analizyng {}...".format(startingPoint))      
        path = getComponentPath(startingPoint)
        #componentType = getComponentType(startingPoint)
        #componentName = os.path.basename(startingPoint)
        skipSubFolders = False
        if arguments["--noSubfolders"]:
            skipSubFolders = True
        
        dependencyReader2(path, jdependency,os.environ['BGEN_ARCH'], skipSubFolders)
            
        #dependencyReader(rootNode2, jdependency,arguments['--force'],os.environ['BGEN_ARCH'])
        #treeToRegeneratePath(regenerateList, rootNode2,jdependency)
    
    for item in jdependency:
        derivedDependencyReader(jdependency, item)
    
    for path in jdependency:
        if "parentName"  in jdependency[path]: 
            continue             
        jlocalConfig = loadConfig(path)
        jdependency[path]["jconfig"] = mergejconfig(jrootConfig, jlocalConfig)
    
    for path in jdependency:
        if "parentName"  not in jdependency[path]:  
            continue  
        jparentConfig = jdependency[jdependency[path]["parentPath"]]["jconfig"]
        jlocalConfig = loadConfig(path)
        jdependency[path]["jconfig"] = mergejconfig(jparentConfig, jlocalConfig)            
        
    
    data={}
    data["buildType"] = buildType
    data['cmakeDefines']={}
    data['cmakeList']={}
    data['cmakes']=[]
    data['targetSystem'] = platform.system().upper()
    data['BGEN_ARCH'] = os.environ['BGEN_ARCH']
    data['runPath'] = runPath
    data['rootPath'] = rootPath
    parents=[]
    children=[]
    for path in jdependency:
        if "parentName"  not in jdependency[path]:
            parents.append(path)
        else:
            children.append(path)

    for path in parents + children:
        item = {"path":path+"/"+"bin","dir":os.getcwd()+"/"+path+"/"+"src","assetPath":path}
        item["dir"] = item["dir"].replace("\\", "/")
        item["jpath"] = path 
        data["cmakes"].append(item)
        if not arguments['--force'] and IsCmakeHardcoded(path):
            print("Skiping generation for: '{}' - CMakeList already exist and is commited.".format(path))
            continue       
        os.chdir(path)           
        item["skipWhenSet"]=getSkipTrigers(jdependency[path]["jconfig"])                
        generateCmakeInCurrentDirectory(path, jdependency, runPath, rootPath, buildType, skipstaticinstall, skipVersionCheck)
        cwdRoot()      

    data["bgen"] = os.path.abspath(__file__).replace("\\", "/") 
    data['jdependency'] = jdependency   
    data['cmakeDefines']={}
    data['cmakeList']={}
    data["installationPrefix"] = getInstallationPrefix(runPath) 
    readSetsFromEnv(data)   
    rootFileName = targetPath+"/"+"CMakeLists.txt"
    f= open(rootFileName,"w+")
    print("Write file: '{}'".format(rootFileName))
    try:
        f.write(Template(cmakeRoot.__doc__).render(data=data))
    except NameError as e:
        print("Error: in template {}".format("cmakeRoot"));    
        print(e)                   
        exit(1)
    f.close()
    os.chdir(runPath)
    return {"jdependency":jdependency,"data":data}
    
def runOnlyOutOfIndex():
    tmp = os.getcwd()
    tmp = tmp.replace("\\", "/")
    level = detectLevel(tmp)    
    if level is not None:
        print("Error: You cannot run this command at {} level.".format(level))
        exit(1)
    #TODO: use is file in git 
    try:
        folderIsInGirIndex = subprocess.check_output(["git", "ls-files", "./"], stderr=subprocess.STDOUT)  
    except subprocess.CalledProcessError as e:
        return
        
    if (len(folderIsInGirIndex)):
        print("Error: You canot run this command in folder which is in the git index.")        
        exit(1)
        
def nameDecorator(mode, name, path):
    if mode == "module":
        return getModuleTargetName(name)
    if mode == "subdir":
        items = path.split("/")   
        return "_".join(items[1:])
    return name
    

def templatebootstrap(component, name):
    runPath = os.getcwd()    
    runPath = runPath.replace("\\", "/")
    cwdRoot()
    if not os.path.isdir(component):
        print("Error: You must be kidding. The component '{}' does not exist.".format(component))
        exit(1)
        
    pathToT= component+"/"+"template"+"/"+name
    if os.path.isdir(pathToT):
        print("Error: You must be kidding. The template '{}' already exist.".format(name))
        exit(1)
        
    level = detectLevel(component)
    if level not in ["FINAL_COMPONNENT","MODULE_COMPONNENT"]:
        print("Error: No way to create template for the '{}'. I only know how to create template for the product, lib or module.".format(component))
        exit(1)
    parentName = nameDecorator(getComponentType(component), os.path.basename(component), component)
    
    data={"nr":1}
    processTempates(os.getcwd()+"/"+pathToT+"/"+"dataSet1"+".json", [{"t":Template(DatasetExample.__doc__), "data":data,"name":"DatasetExample"}])
    data={"nr":2}
    processTempates(os.getcwd()+"/"+pathToT+"/"+"dataSet2"+".json", [{"t":Template(DatasetExample.__doc__), "data":data,"name":"DatasetExample"}])
    data={"component":component, "parentName": parentName}
    processTempates(os.getcwd()+"/"+pathToT+"/"+name+".thpp", [{"t":Template(TemplateExample.__doc__), "data":data,"name":"TemplateExample"}])
        

        
    #getModuleTargetName(os.path.basename(os.getcwd()))
    #kukukuku

    os.chdir(runPath)
      
def gtestbootstrap(component, name):
    runPath = os.getcwd()    
    runPath = runPath.replace("\\", "/")
    cwdRoot() 
    libraryNeeds = "3rdParty/gtest"
    rd = ReleaseDrive(os.environ['BGEN_RELEASE_PATH'])   
    gtestVersions = rd.getVersions(libraryNeeds, os.environ['BGEN_ARCH'])
    if (len(gtestVersions) == 0):
        print("Error: unable to find {}' library on release drive.".format(libraryNeeds))
        print("    Please add it ad run command again.")
        exit(1)
    latestGtestVersion = gtestVersions[-1]
    
    
    if not os.path.isdir(component):
        print("Error: You must be kidding. The component '{}' does not exist.".format(component))
        exit(1)
    pathToG= component+"/"+"gtest"+"/"+name
    if os.path.isdir(pathToG):
        print("Error: You must be kidding. The gtest '{}' already exist.".format(name))
        exit(1)
        
    level = detectLevel(component)
    if level not in ["FINAL_COMPONNENT","MODULE_COMPONNENT"]:
        print("Error: No way to create gtest for the '{}'. I only know how to create gtest for the product, lib or module.".format(component))
        exit(1)     
        
    pathToSrcG = pathToG+"/"+"src";
    os.makedirs(pathToSrcG, exist_ok=True)
    data={"name":name,"parentName":os.path.basename(component)}
    processTempates(os.getcwd()+"/"+pathToSrcG+"/"+name+".cpp", [{"t":Template(GtestSource.__doc__), "data":data,"name":"GtestSource"}])

    processTempates(os.getcwd()+"/"+pathToG+"/"+"dependency.json", [{"t":Template(GtestDependency.__doc__), "data":{"gtestVersion":latestGtestVersion},"name":"GtestDependency"}])
    print("Info: Gtest has been generated")   

    os.chdir(runPath)
 
def productbootstrap(name):
    runPath = os.getcwd()    
    runPath = runPath.replace("\\", "/")
    cwdRoot() 
    arch = os.environ['BGEN_ARCH']
    pathToProduct = os.getcwd()+ "/product/"+name;
    if os.path.isdir(pathToProduct):
        print("Error: product '{}' already exist.".format(name));
        os.chdir(runPath);
        exit(1);
    data={"name":name,"arch":arch}
    processTempates(pathToProduct+"/src/main.cpp", [{"t":Template(ProductSource.__doc__), "data":data,"name":"ProductSource"}])
    processTempates(pathToProduct+"/dependency.json", [{"t":Template(ProductDependency.__doc__), "data":data,"name":"ProductDependency"}])
    processTempates(pathToProduct+"/version.json", [{"t":Template(VersionPattern.__doc__), "data":data,"name":"VersionPattern"}])
    processTempates(pathToProduct+"/resource/dir1/myResour.txt", [{"t":Template(ProductResource1Example.__doc__), "data":data,"name":"ProductResource1Example"}])
    processTempates(pathToProduct+"/install/bin/myInstal.txt", [{"t":Template(ProductInstall1Example.__doc__), "data":data,"name":"ProductInstall1Example"}])
    print("Info: Product generated", pathToProduct+"/src/main.cpp")   
    os.chdir(runPath)
    
def modulebootstrap(name):
    runPath = os.getcwd()    
    runPath = runPath.replace("\\", "/")
    cwdRoot() 
    arch = os.environ['BGEN_ARCH']
    pathToProduct = os.getcwd()+ "/module/"+name;
    if os.path.isdir(pathToProduct):
        print("Error: modul '{}' already exist.".format(name));
        os.chdir(runPath);
        exit(1);
    data={"name":name,"mode":"module","arch":arch}
    processTempates(pathToProduct+"/src/"+name+".cpp", [{"t":Template(ModuleLibSource.__doc__), "data":data,"name":"ModuleLibSource"}])
    processTempates(pathToProduct+"/src/public_headers/"+name+"/"+name+".hpp", [{"t":Template(ModuleLibHeader.__doc__), "data":data,"name":"ModuleLibHeader"}])
    processTempates(pathToProduct+"/dependency.json", [{"t":Template(ProductDependency.__doc__), "data":data,"name":"ProductDependency"}])
    processTempates(pathToProduct+"/version.json", [{"t":Template(VersionPattern.__doc__), "data":data,"name":"VersionPattern"}])
    processTempates(pathToProduct+"/resource/dir1/myResour.txt", [{"t":Template(ProductResource1Example.__doc__), "data":data,"name":"ProductResource1Example"}])
    processTempates(pathToProduct+"/install/bin/myInstal.txt", [{"t":Template(ProductInstall1Example.__doc__), "data":data,"name":"ProductInstall1Example"}])
    print("Info: Module generated", pathToProduct+"/src/main.cpp")   
    os.chdir(runPath)
    
def resourceCompileMode(fileName, outputFile):
    if outputFile == None:
        outputFile = fileName
        
    resCompiler(fileName, outputFile)
    
def runCmakeFromEnv(data, default, envVarName, flags=""):
    if envVarName in os.environ:
        cmd = os.environ[envVarName] + " "+ flags
    else:
        cmd = default + " " + flags
     
    cmd = Template(cmd).render(data=data)
    print("Execute:" + cmd)
    exitStatus = os.system(cmd)
    if (exitStatus !=0 ):
        print("Error: cmake has an error. {}".format(exitStatus))
        exit(exitStatus)

class TestLogger:
    def __init__(self,suiteName):
        self.f = open("bgenReport.xml", "w")
        self.f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
        self.f.write("<testsuites>\n")
        self.f.write('<testsuite name="{}" tests="8" time="0.0" >'.format(suiteName))
    def addTest(self, t):
        return
    def startTest(self,t):
        self.f.write('<testcase name="{}" classname="{}" time="{}">\n'.format(t['name'],"gtest","0.0"))
        return
    def passTest(self,t):
        self.f.write("</testcase>\n")
        return 
    def failedTest(self,t):
        
        self.f.write('<failure message="{}">\n'.format(t['result']))
        self.f.write(t['stderr'])
        self.f.write(t['stdout'])
        self.f.write('</failure>\n')
        self.f.write("</testcase>\n")
        return 
    def done(self):
        self.f.write("</testsuite>\n")
        self.f.write("</testsuites>\n")
        self.f.close()
        return
    
def runTest(testLogger, pattern):
    print("test")
    runPath = os.getcwd()
    runPath = runPath.replace("\\", "/")
    installPrefix = getInstallationPrefix(runPath)+os.sep+getTestRootSuffix()
    if platform.system().upper() == "WINDOWS":
        ext = ".exe"
    else:
        ext = ""
    testToExecute = []
    print("Searching gtests...") 
    for item in glob.glob(installPrefix + os.sep+ "/**/*_gtest_*"+ext, recursive=True):
        if  item.endswith('.xml') or item.endswith('.log') or item.endswith('.sh') or item.endswith('.cfg') or item.endswith('.json'):
            continue
        testLogger.addTest(item)
        testName = item.split(os.sep)
        testName = os.sep.join(testName[len(testName)-5:])
        if (testName in testToExecute):
            print("Error: Test name dupliates. '{}'".format(testName))
            continue
        testToExecute.append({"path":item,"name":testName,"cmd":pattern.replace("{}", item)})
    print("Found {} gtests".format(len(testToExecute)))
    passTests  = []
    failedTests = []
    for t in testToExecute:    
        print("Start '{}' test".format(t["path"]))
        testLogger.startTest(t) 
        try:
            #process = subprocess.Popen(t["cmd"].split(" "),stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
            #process = subprocess.Popen(t["cmd"].split(" "),stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,bufsize=1)
            if VERBOSE:
                (ret1, (stdout1, stderr1)) = run_cmd(["env"])

            (ret, (stdout, stderr)) = run_cmd(t["cmd"].split(" ") )
        
            if ret != 0:
                t["result"] = "Failed (exitcode: {})".format(ret)
                t["stderr"] = stderr
                t["stdout"] = stdout
                testLogger.failedTest(t) 
                failedTests.append(t)
            else:
                testLogger.passTest(t) 
                t["result"] = "Pass"
                passTests.append(t)
        except FileNotFoundError:
            t["result"] = "Failed (file not found)"
            t["stderr"] = stderr
            t["stdout"] = stdout
            testLogger.failedTest(t) 
            failedTests.append(t)
        #print("{} {}".format(t["cmd"],t["result"]))
        
    
    if len(failedTests):
        print("List of failed tests:")
    maxLen = 0
    for t in failedTests:
        maxLen = max({maxLen,len(t["path"])})
   
    for t in failedTests:
        print("   {:<{}} {}".format(t["cmd"], maxLen+3, t["result"]))
        
    if len(failedTests):
        print("Error: ",end="")
    else:
        print("Success: ",end="")    
    
    
    print(" {} test Failed, {} test Pass.".format(len(failedTests),len(passTests)))
     
    testLogger.done();
    if (len(failedTests) > 0):
        exit (1)
    

def writeHeader():
    ret = ""
    ret += "/*******************************************************\n"
    ret += "* Copyright (C) {} Bgen\n".format(time.strftime("%Y"))
    ret += "*\n"
    ret += "*  Automatically generated by bgen\n"
    ret += "*  Created on {}\n".format(time.strftime("%m/%d/%Y"))
    ret += "*\n"
    ret += "*******************************************************/\n"
    return ret

def gitHasCommits():
    status = shellCmd("git status", quiet = True)
    statusit = status.splitlines()
    for line in statusit:
        matchObj = re.match( r'.*No commits yet.*', line)
        if matchObj:
            return False
    return True

def getGitInfo():
    change = shellCmd("git diff --quiet --exit-code", quiet = True)
    if change == None:
        change = "(+)"
    else:
        change = ""
    tag = shellCmd("git describe --exact-match --tags", quiet = True)
    if tag == None:
        tag = ""

    if gitHasCommits():
        revision = shellCmd("git log --pretty=format:'%H' -n 1", quiet = False)
        revision = revision[1:len(revision)-1]
    else:
        revision = "No commits yet"

    remote = shellCmd("git config --get remote.origin.url", quiet = True)
    if remote == "":
        remote = "??"

    ret = {
          "remote":remote,
          "branch": shellCmd("git branch --show-current", quiet = False),
          "tag": tag,
          "revision": revision + change,
          "shortRev":  revision[0:8] + change
          }
    return ret

def generateVersion(suffix, inputFile, outputFile, fromGit, bgenVersion, installationSuffix):
    data={"year":time.strftime("%Y")}
    gitInfo = {}

    if fromGit:
        if IsGitRepo(os.getcwd()):
            gitInfo = getGitInfo()
        else:
            gitInfo["tag"] = "??"
            gitInfo["branch"] = "??"
            gitInfo["revision"] = "??"
            gitInfo["shortRev"] = "??"
        today = date.today()
        data.update({"BGEN_DATE":today.strftime("%d/%m/%Y"),"BGEN_USERNAME":getUserName(), "BGEN_VERSION":bgenVersion, "BGEN_TAG":gitInfo["tag"], "BGEN_BRANCH":gitInfo["branch"], "BGEN_REVISION": gitInfo["revision"], "BGEN_REVISION_SHORT": gitInfo["shortRev"]})

        processTempates(outputFile + ".h", [{"t":Template(Git_h.__doc__), "data":data,"name":"Git_h"}])
        processTempates(outputFile + "Def.h", [{"t":Template(GitDef_h.__doc__), "data":data,"name":"GitDef_h"}])
    else:
        data["suffix"] = suffix
        data["installationSuffix"] = installationSuffix
        jversion = versionReader(os.path.dirname(inputFile)+"/"+VERSION_FILE_NAME)
        data["version"] = jversion["version"]
        processTempates(outputFile + ".h", [{"t":Template(Version_h.__doc__), "data":data,"name":"Version_h"}])
        processTempates(outputFile + ".c", [{"t":Template(Version_c.__doc__), "data":data,"name":"Version_c"}])

    
def startDiff(a ,b):
    res = 0
    fileA = open(a, 'r')
    fileB = open(b, 'r')
  
        
    for line in difflib.unified_diff( [s.rstrip("\n\r") for s in fileA.readlines()], [s.rstrip("\n\r") for s in fileB.readlines()], fromfile=a, tofile=b):
        res = 1
        print(line)
        
    fileA.close()
    fileB.close()
    
    print("exit = {}".format(res));
    exit(res)

def getExternalIncludes(jdependency):
    includes = []
    for path,value in jdependency.items():
        if "data" not in  value:
            continue
        includePath = value["data"]["runPath"]+"/"+path + "/bin/generated"
        if includePath not in includes:
            includes.append(includePath)
        for key,spec in value["data"]["libSpecs"].items():
            for includePath in spec["include_directories"]:
                if includePath not in includes:
                    includes.append(includePath)
        
    return{"includePaths":includes}

def generateEclipseIncludePath(includePaths):
    processTempates("eclipseIncludePaths.xml", [{"t":Template(EclipseIncludPath.__doc__), "data":includePaths,"name":"EclipseIncludPath"}])


def dumpDependency(data):
    for path in data["jdependency"]:
        print("-----------------------------------------------------------------")
        print(path)
        for prop in data["jdependency"][path]:
            #print("    {} ".format(prop))#data["jdependency"][path][prop]
            if prop in ["dependencyPaths","derived_dependency"]:
                print("    {} ".format(prop))
                for value in data["jdependency"][path][prop]:
                    print("        {}".format(value))
            if prop in ["dependency"]:
                print("    {} ".format(prop))
                for value in data["jdependency"][path][prop]:
                    print("        {}/{}".format(value["type"], value["name"]))                    
                #print("        {}/{}".format(data["jdependency"][path][prop]["type"], data["jdependency"][path][prop]["name"]))
def getAssetStatus():
    cwdRoot() 
    changes = iter(shellCmd("git status -s").splitlines())
    for item in changes:
        tmp = item[:2]       
        f = item[2:] 
        print(tmp,f)

def readJsonFile(fileName):
    try:
        with open(fileName, 'r') as f:
            return json.load(f)
    except IOError:
        print ("Error: Could not read file {}:".format(fileName))
        exit(1)
    except ValueError as e:
        print ("Error: wrong json format in file: '{}'".format(fileName))
        print(e)
        exit(1)

def readTxtFile(fileName):
    try:
        with open(fileName, 'r') as f:
            return f.read()
    except IOError:
        print ("Error: Could not read file {}:".format(fileName))
        exit(1)
        
def readCsvFile(fileName):
    try:
        with open(fileName, newline='', encoding='utf-8-sig') as csvfile:
            reader = csv.reader(csvfile)
            header=[]    
            ret = []
            for row in reader:
                if (len(header) == 0):
                    header = row                       
                else:
                    tmp={}
                    for x in range(len(header)):
                        tmp[header[x]] = row[x]
                    ret.append(tmp)                                          
            return ret
    except IOError:
        print ("Error: Could not read file {}:".format(fileName))
        exit(1)            

    
def readFile(fileName):
    filename, file_extension = os.path.splitext(fileName)
    if file_extension == ".json":
        return readJsonFile(fileName)
    if file_extension == ".csv":
        x = {"csv":readCsvFile(fileName)}       
        return x
                    
    return readTxtFile(fileName)

    
def runPreprocess(templateFile, inputFiles, outputFile, dryRun, inputDir, splitByName , settings):
    if not settings or len(settings) == 0:
        settings = '{}'

    jsettings = json.loads(settings)
    if inputDir:
        inputFiles=[]
        for filename in os.listdir(inputDir):
            if filename.endswith(".json") or filename.endswith(".cpp"): 
                inputFiles.append(os.path.join(inputDir, filename))
        if len(inputFiles) == 0:
            print("Error: input file does not exist. There are no 'json' files in the '{}' directory.".format(inputDir))
            exit(1)

 
    if dryRun:
        print(outputFile)
        return

    if (VERBOSE):
        print("Preprocessing '{}' file...".format(templateFile))
    outputFiles = []

    dataFiles={}
    for item in inputFiles:
        if (VERBOSE):
            print("   reading input file: {} ...".format(item))
        jconfig = readFile(item)
        iPath = item
        if not os.path.isabs(str(iPath)):
            iPath = os.path.abspath(os.getcwd()+"/"+str(iPath))
        
        iFile = os.path.basename(item)          
        iName = os.path.splitext(iFile)[0]


        tPath = templateFile
        if not os.path.isabs(str(tPath)):
            tPath = os.path.abspath(os.getcwd()+"/"+str(tPath))

        tFile = os.path.basename(templateFile)
        tName =  os.path.splitext(tFile)[0]
   
        if (splitByName):
            oPath = splitByName.replace("{iName}",iName)
            oPath = oPath.replace("{tName}",tName)
            oFile =  os.path.basename(oPath)
            oName =  os.path.splitext(oFile)[0]
     
        else:
            oFile = os.path.basename(outputFile)
            oPath = outputFile
            oName =  os.path.splitext(oFile)[0]
        
        if oPath not in outputFiles:
            outputFiles.append(oPath)
        jconfig["iPath"] = iPath
        jconfig["iFile"] = iFile
        jconfig["iName"] = iName

        jconfig["tPath"] = tPath
        jconfig["tFile"] = tFile
        jconfig["tName"] = tName

        jconfig["oPath"] = oPath
        jconfig["oFile"] = oFile
        jconfig["oName"] = oName

        dataFiles[item] = jconfig

    t = readTxtFile(templateFile)
    agregat = {}
    
    
    if dryRun:
        for item in outputFiles:
            print(item)
        return


    for key,value in dataFiles.items():
        if value["oPath"] not in agregat:
             agregat[value["oPath"]] = []
        agregat[value["oPath"]].append({"t":Template(t),"data":{**value, **jsettings},"name":templateFile})
   

 
    for key,value in agregat.items():
        processTempates(key, value)


#***********************************************************************
#*                       MAIN                                          *
#***********************************************************************
if __name__ == '__main__':    
    version = "bgen 5.0.0"
    arguments = docopt(__doc__, version=version)
    genMode = True
#     print(arguments)     
    VERBOSE = arguments['--verbose']
    FORCE = arguments['--force']
    if arguments['<components>']:
        for item in range(0, len(arguments['<components>'])):
           arguments['<components>'][item] = arguments['<components>'][item].replace("\\", "/")

    if arguments['diff']:
        startDiff(arguments['<fileA>'],arguments['<fileB>'])
        genMode = False
    if arguments['show']:
        checkEnvSettings()       
        startShow(arguments['<components>'], arguments['--all'], arguments['--changed'], arguments['<starting_sha>'], arguments['--parent'], arguments['--hard'], arguments['--list'], arguments['--refresh'], arguments['--add'], arguments['--remove'])
        genMode = False
    elif arguments['res']:
        resourceCompileMode(arguments["<fileName>"],arguments["<outputFile>"])
        genMode = False
    elif arguments['ver']:
        generateVersion(arguments["--suffix"], arguments["--input"],arguments["--out"], arguments["--git"], version, arguments["--installationSuffix"])
        genMode = False
    elif arguments['bootstrap'] and arguments['gtest']:
        gtestbootstrap(arguments['<component>'], arguments['--name'])
        genMode = False
    elif arguments['bootstrap'] and arguments['product']:
        productbootstrap(arguments['--name']);
        genMode = False
    elif arguments['bootstrap'] and arguments['module']:
        modulebootstrap(arguments['--name']);
        genMode = False
    elif arguments['bootstrap'] and arguments['template']:
        templatebootstrap(arguments['<component>'], arguments['--name'])
        genMode = False
        
    elif arguments['preprocess']:
        #//--template=<template> --inputs=<json>... --output=<outputFile> [--dryRun]
        runPreprocess(arguments["--template"], arguments["--inputs"], arguments["--output"], arguments["--dryRun"],  arguments["--inputDir"], arguments["--splitByName"], arguments["--settings"])
        genMode = False
    elif arguments['build']:
        checkEnvSettings()
        flags = ""
        if arguments['-j']:
            flags = "-j " + arguments['<jobs>']

        #jdependency = readJsonFile(JDEPENDENCY_FILE)
        data= readJsonFile(JDATA_FILE)
        runCmakeFromEnv({"buildType":data["buildType"]},'make', "BGEN_BUILD_CMD" , flags)
        genMode = False
    if arguments['install']:
        checkEnvSettings()
        data= readJsonFile(JDATA_FILE)
        runCmakeFromEnv({"buildType":data["buildType"]},'make install', "BGEN_INSTALL_CMD")
        genMode = False
    if arguments['test']:
        tl = TestLogger(os.getcwd())

        if arguments['--cmd']:
            pattern = arguments["<pattern>"]
        else:
            pattern = "{}"
        runTest(tl,pattern)
        genMode = False
    if (genMode):
        print(version)
        if (arguments["--debug"]):
            buildType="Debug"
        else:
            buildType="Release"
        
        checkEnvSettings()
        runOnlyOutOfIndex()
        skipstaticinstall = not arguments['--staticinstall']
        ret = startCmakeGen(buildType, skipstaticinstall);
        
        data = ret["data"]; 
        jdependency = ret["jdependency"]      
        if arguments['<path>']:
            arguments['<path>'] = arguments['<path>'].replace("\\", "/")
            flags = "";
            if arguments['--cmakeflags']:
               flags = arguments['--cmakeflags']
            if arguments['--debug']:
               flags = flags + " -DCMAKE_BUILD_TYPE=Debug"
            runCmakeFromEnv({"path":arguments['<path>']},'cmake ${data["path"]} ', "BGEN_GEN_CMD", flags)
        else:
            print("Warning: dont know how to run cmake. Do it manually.")
    
        if VERBOSE:
            dumpDependency(data)
            

        includePaths = getExternalIncludes(jdependency);
        generateEclipseIncludePath(includePaths);
                    
       
        del data["jdependency"]
        x = json.JSONEncoder(indent=4).encode(data)
        writeFileIfChanged("data.json", x)
        x = json.JSONEncoder(indent=4).encode(jdependency)
        writeFileIfChanged("jdependency.json", x)


        

        print("Done.")
    
    
#TODO:
   





