cmake_minimum_required(VERSION 3.10)

project(libasr)

if (NOT CMAKE_CXX_STANDARD)
    set(CMAKE_CXX_STANDARD 17
        CACHE STRING "C++ standard" FORCE)
endif ()

if (NOT LFORTRAN_VERSION)
    set(LFORTRAN_VERSION "0.1-git"
        CACHE STRING "LFortran version" FORCE)
endif ()

# Generated into the binary directory, not the source tree: several build
# directories (native, build-wasm, the bootstrap in wasm-build0.sh) configure
# the same source tree, and writing here would leave whichever configured last
# in charge -- a wasm configure enabling HAVE_LFORTRAN_XEUS then breaks the
# native link with an undefined run_kernel. ${libasr_BINARY_DIR}/.. is already
# a public include directory below.
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

if (NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
    foreach(header IN ITEMS lfortran_intrinsics.h lfortran_stdin.h ISO_Fortran_binding.h)
        configure_file(
            "${CMAKE_CURRENT_SOURCE_DIR}/runtime/${header}"
            "${CMAKE_CURRENT_BINARY_DIR}/runtime/${header}"
            COPYONLY
        )
    endforeach()
endif()

set(SRC
    codegen/asr_to_cpp.cpp
    codegen/asr_to_c.cpp
    codegen/asr_to_julia.cpp
    codegen/asr_to_python.cpp
    codegen/asr_to_fortran.cpp
    codegen/asr_to_py.cpp
    codegen/x86_assembler.cpp
    codegen/asr_to_x86.cpp
    codegen/asr_to_wasm.cpp
    codegen/wasm_to_wat.cpp
    codegen/wasm_to_x86.cpp
    codegen/wasm_to_x64.cpp
    codegen/wasm_utils.cpp

    pass/nested_vars.cpp
    pass/array_struct_temporary.cpp
    pass/function_result_scope.cpp
    pass/where.cpp
    pass/function_call_in_declaration.cpp
    pass/dtio_callbacks.cpp
    pass/array_passed_in_function_call.cpp
    pass/string_length_arguments.cpp
    pass/string_format_array_args.cpp
    pass/openmp.cpp
    pass/parallel_canonicalize.cpp
    pass/parallel_dispatch.cpp
    pass/param_to_const.cpp
    pass/do_loops.cpp
    pass/for_all.cpp
    pass/while_else.cpp
    pass/global_stmts.cpp
    pass/select_case.cpp
    pass/init_expr.cpp
    pass/global_init.cpp
    pass/implied_do_loops.cpp
    pass/array_op.cpp
    pass/subroutine_from_function.cpp
    pass/transform_optional_argument_functions.cpp
    pass/class_constructor.cpp
    pass/print_arr.cpp
    pass/print_struct_type.cpp
    pass/print_list_tuple.cpp
    pass/pass_utils.cpp
    pass/unused_functions.cpp
    pass/flip_sign.cpp
    pass/div_to_mul.cpp
    pass/replace_symbolic.cpp
    pass/intrinsic_function.cpp
    pass/intrinsic_subroutine.cpp
    pass/fma.cpp
    pass/loop_vectorise.cpp
    pass/sign_from_value.cpp
    pass/inline_function_calls.cpp
    pass/scoped_inlining.cpp
    pass/loop_unroll.cpp
    pass/dead_code_removal.cpp
    pass/instantiate_template.cpp
    pass/update_array_dim_intrinsic_calls.cpp
    pass/pass_array_by_data.cpp
    pass/pass_list_expr.cpp
    pass/pass_compare.cpp
    pass/unique_symbols.cpp
    pass/intent_out_deallocate.cpp
    pass/promote_allocatable_to_nonallocatable.cpp
    pass/conditional_expr.cpp
    pass/replace_with_compile_time_values.cpp
    pass/gpu_decline.cpp
    pass/gpu_unsupported_check.cpp
    pass/gpu_offload.cpp
    pass/gpu_offload_associate.cpp
    pass/gpu_offload_body.cpp
    pass/gpu_offload_decide.cpp
    pass/gpu_offload_launch.cpp
    pass/gpu_offload_liveout.cpp
    pass/gpu_offload_overwrite.cpp
    pass/gpu_offload_host_association.cpp
    pass/gpu_offload_privatize.cpp
    pass/gpu_procedure_implementation.cpp
    pass/gpu_offload_stop.cpp
    pass/gpu_offload_component_fit.cpp
    pass/gpu_offload_designator.cpp
    pass/gpu_offload_preflight.cpp
    pass/gpu_offload_rewrite.cpp
    pass/gpu_offload_collect.cpp
    pass/gpu_offload_device_fn.cpp
    pass/gpu_offload_intrinsics.cpp
    pass/gpu_offload_array_ops.cpp
    pass/gpu_offload_import.cpp
    pass/gpu_offload_kernel_scope.cpp
    pass/device_partition.cpp
    pass/gpu_kernel_abi.cpp
    pass/gpu_kernel_plan.cpp
    pass/gpu_data_layout.cpp
    pass/device_launch_expand.cpp
    pass/gpu_launch_sync.cpp
    pass/gpu_affine.cpp
    pass/gpu_loop_utils.cpp
    pass/gpu_loop_fusion.cpp
    pass/gpu_time_skew.cpp
    pass/gpu_index_set_split.cpp
    pass/gpu_array_statements.cpp
    pass/dead_array_store.cpp
    pass/gpu_vector_lanes.cpp
    pass/gpu_memory_space.cpp
    pass/gpu_device_workspace.cpp
    pass/gpu_struct_component_args.cpp
    pass/gpu_device_heap.cpp
    pass/gpu_device_struct_storage.cpp
    pass/coarray.cpp

    codegen/asr_to_metal.cpp
    codegen/asr_to_cuda.cpp

    asr_verify.cpp
    asr_utils.cpp
    casting_utils.cpp
    asr_scopes.cpp
    asr_text.cpp
    asr_text_parser.cpp
    modfile.cpp
    pickle.cpp
    serialization.cpp
)
if (WITH_LLVM)
    set(SRC ${SRC}
        codegen/evaluator.cpp
        codegen/asr_to_llvm.cpp
        codegen/llvm_array_utils.cpp
        codegen/llvm_utils.cpp
    )
    if (WITH_MLIR)
        set(SRC ${SRC} codegen/asr_to_mlir.cpp)
    endif()
    # We use deprecated API in LLVM, so we disable the warning until we upgrade
    if (NOT MSVC)
        set_source_files_properties(codegen/evaluator.cpp PROPERTIES
            COMPILE_FLAGS -Wno-deprecated-declarations)
        set_source_files_properties(codegen/asr_to_llvm.cpp PROPERTIES
            COMPILE_FLAGS -Wno-deprecated-declarations)
        set_source_files_properties(codegen/llvm_array_utils.cpp PROPERTIES
            COMPILE_FLAGS -Wno-deprecated-declarations)
        set_source_files_properties(codegen/llvm_utils.cpp PROPERTIES
            COMPILE_FLAGS -Wno-deprecated-declarations)
        set_source_files_properties(stacktrace.cpp PROPERTIES
            COMPILE_FLAGS -Wno-deprecated-declarations)
    endif()
endif()

set(LFORTRAN_UTILS
  assert.h
  colors.h
  exception.h
  location.h

  diagnostics.h
  diagnostics.cpp
  stacktrace.h
  stacktrace.cpp
  string_utils.cpp
  utils.h
  utils2.cpp
)
add_library(lfortran_utils OBJECT ${LFORTRAN_UTILS})
target_include_directories(lfortran_utils BEFORE PUBLIC ${libasr_SOURCE_DIR}/..)
target_include_directories(lfortran_utils BEFORE PUBLIC ${libasr_BINARY_DIR}/..)

add_library(asr STATIC ${SRC})
target_include_directories(asr BEFORE PUBLIC ${libasr_SOURCE_DIR}/..)
target_include_directories(asr BEFORE PUBLIC ${libasr_BINARY_DIR}/..)
target_link_libraries(asr lfortran_utils)
if (WITH_LIBUNWIND)
    target_link_libraries(asr p::libunwind)
endif()
if (WITH_BFD)
    target_link_libraries(asr p::bfd)
endif()
if (WITH_LINK)
    target_link_libraries(asr p::link)
endif()
if (WITH_EXECINFO)
    target_link_libraries(asr p::execinfo)
endif()
if (WITH_LLVM)
    target_link_libraries(asr p::llvm)
    target_link_libraries(lfortran_utils p::llvm)
    if (EMSCRIPTEN)
        target_link_libraries(asr lldWasm lldCommon)
    endif()
endif()

# The driver also locates these scripts relative to the build-tree executable.
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
    configure_file(dwarf_convert.py dwarf_convert.py COPYONLY)
    configure_file(dat_convert.py dat_convert.py COPYONLY)
endif()

install(
    FILES dwarf_convert.py dat_convert.py
    PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ
    DESTINATION ${CMAKE_INSTALL_PREFIX}/share/lfortran
)

# Relative to runtime/. The CPU emulation of the CUDA runtime API (cuda_cpu/)
# keeps its own directory, which holds its cuda_runtime.h.
set(LFORTRAN_GPU_RUNTIME_SOURCES
    lfortran_gpu_metal.m
    lfortran_gpu_cuda.c
    lfortran_gpu_runtime.h
    cuda_cpu/cuda_runtime.h
    cuda_cpu/cuda_cpu_runtime.c
    cuda_cpu/cuda_cpu_device.h
    cuda_cpu/cuda_cpu_emulator.h
)

# The driver compiles these sources when linking GPU programs. Keep the
# build-tree and installed layouts consistent with its runtime lookup path.
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
    set(LFORTRAN_GPU_RUNTIME_COPIES)
    foreach(name IN LISTS LFORTRAN_GPU_RUNTIME_SOURCES)
        set(source "${CMAKE_CURRENT_SOURCE_DIR}/runtime/${name}")
        set(destination "${CMAKE_CURRENT_BINARY_DIR}/runtime/${name}")
        get_filename_component(destination_dir "${destination}" DIRECTORY)
        add_custom_command(
            OUTPUT "${destination}"
            COMMAND ${CMAKE_COMMAND} -E make_directory "${destination_dir}"
            COMMAND ${CMAKE_COMMAND} -E copy_if_different
                "${source}" "${destination}"
            DEPENDS "${source}"
            VERBATIM)
        list(APPEND LFORTRAN_GPU_RUNTIME_COPIES "${destination}")
    endforeach()
    add_custom_target(asr_gpu_runtime_sources ALL
        DEPENDS ${LFORTRAN_GPU_RUNTIME_COPIES})
    add_dependencies(asr asr_gpu_runtime_sources)
endif()

get_filename_component(LFORTRAN_GPU_RUNTIME_INSTALL_PARENT
    "${CMAKE_INSTALL_LIBDIR}" DIRECTORY)
if (LFORTRAN_GPU_RUNTIME_INSTALL_PARENT)
    set(LFORTRAN_GPU_RUNTIME_INSTALL_DIR
        "${LFORTRAN_GPU_RUNTIME_INSTALL_PARENT}/libasr/runtime")
else()
    set(LFORTRAN_GPU_RUNTIME_INSTALL_DIR "libasr/runtime")
endif()
foreach(name IN LISTS LFORTRAN_GPU_RUNTIME_SOURCES)
    get_filename_component(subdir "${name}" DIRECTORY)
    install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/runtime/${name}"
        DESTINATION "${LFORTRAN_GPU_RUNTIME_INSTALL_DIR}/${subdir}")
endforeach()
