Write all source files to the compile_dir

Property definitions

nitc $ MakefileToolchain :: write_files
	# Write all source files to the `compile_dir`
	fun write_files(compile_dir: String, cfiles: Array[String])
	do
		var platform = compiler.target_platform
		if platform.supports_libunwind then compiler.build_c_to_nit_bindings
		var cc_opt_with_libgc = "-DWITH_LIBGC"
		if not platform.supports_libgc then cc_opt_with_libgc = ""

		# Add gc_choser.h to aditionnal bodies
		var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
		if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
		compiler.extern_bodies.add(gc_chooser)
		var clib = toolcontext.nit_dir / "clib"
		compiler.files_to_copy.add "{clib}/gc_chooser.c"
		compiler.files_to_copy.add "{clib}/gc_chooser.h"

		# Add lttng traces provider to external bodies
		if toolcontext.opt_trace.value then
			#-I. is there in order to make the TRACEPOINT_INCLUDE directive in clib/traces.h refer to the directory in which gcc was invoked.
			var traces = new ExternCFile("traces.c", "-I.")
			traces.pkgconfigs.add "lttng-ust"
			compiler.extern_bodies.add(traces)
			compiler.files_to_copy.add "{clib}/traces.c"
			compiler.files_to_copy.add "{clib}/traces.h"
		end

		# FFI
		for m in compiler.mainmodule.in_importation.greaters do
			compiler.finalize_ffi_for_module(m)
		end

		# Copy original .[ch] files to compile_dir
		for src in compiler.files_to_copy do
			var basename = src.basename
			var dst = "{compile_dir}/{basename}"
			src.file_copy_to dst
		end

		var hfilename = compiler.header.file.name + ".h"
		var hfilepath = "{compile_dir}/{hfilename}"
		var h = new FileWriter.open(hfilepath)
		for l in compiler.header.decl_lines do
			h.write l
			h.write "\n"
		end
		for l in compiler.header.lines do
			h.write l
			h.write "\n"
		end
		h.close

		var max_c_lines = toolcontext.opt_max_c_lines.value
		for f in compiler.files do
			var i = 0
			var count = 0
			var file: nullable FileWriter = null
			for vis in f.writers do
				if vis == compiler.header then continue
				var total_lines = vis.lines.length + vis.decl_lines.length
				if total_lines == 0 then continue
				count += total_lines
				if file == null or (count > max_c_lines and max_c_lines > 0) then
					i += 1
					if file != null then file.close
					var cfilename = "{f.name}.{i}.c"
					var cfilepath = "{compile_dir}/{cfilename}"
					self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
					cfiles.add(cfilename)
					file = new FileWriter.open(cfilepath)
					file.write "#include \"{f.name}.0.h\"\n"
					count = total_lines
				end
				for l in vis.decl_lines do
					file.write l
					file.write "\n"
				end
				for l in vis.lines do
					file.write l
					file.write "\n"
				end
			end
			if file == null then continue
			file.close

			var cfilename = "{f.name}.0.h"
			var cfilepath = "{compile_dir}/{cfilename}"
			var hfile: nullable FileWriter = null
			hfile = new FileWriter.open(cfilepath)
			hfile.write "#include \"{hfilename}\"\n"
			for key in f.required_declarations do
				if not compiler.provided_declarations.has_key(key) then
					var node = compiler.requirers_of_declarations.get_or_null(key)
					if node != null then
						node.debug "No provided declaration for {key}"
					else
						print "No provided declaration for {key}"
					end
					abort
				end
				hfile.write compiler.provided_declarations[key]
				hfile.write "\n"
			end
			hfile.close
		end

		self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
	end
src/compiler/abstract_compiler.nit:227,2--333,4

nitc $ AndroidToolchain :: write_files
	redef fun write_files(compile_dir, cfiles)
	do
		var android_project_root = android_project_root.as(not null)
		var android_app_root = android_project_root/"app"
		var project = new AndroidProject(toolcontext.modelbuilder, compiler.mainmodule)
		var release = toolcontext.opt_release.value

		# Compute the root of the project where could be assets and resources
		var project_root = "."
		var mpackage = compiler.mainmodule.first_real_mmodule.mpackage
		if mpackage != null then
			var root = mpackage.root
			if root != null then
				var filepath = root.filepath
				if filepath != null then
					project_root = filepath
				end
			end
		end

		# Gather app configs
		# ---

		var app_name = project.name
		var app_package = project.namespace
		var app_version = project.version

		var app_min_api = project.min_api
		if app_min_api == null then app_min_api = 10

		var app_target_api = project.target_api
		if app_target_api == null then app_target_api = app_min_api

		var app_max_api = ""
		if project.max_api != null then app_max_api = "maxSdkVersion  {project.max_api.as(not null)}"

		# Create basic directory structure
		# ---

		android_project_root.mkdir
		android_app_root.mkdir
		(android_app_root/"libs").mkdir

		var android_app_main = android_app_root / "src/main"
		android_app_main.mkdir
		(android_app_main / "java").mkdir

		# /app/build.gradle
		# ---

		# Use the most recent build_tools_version
		var android_home = "ANDROID_HOME".environ
		if android_home.is_empty then android_home = "HOME".environ / "Android/Sdk"
		var build_tools_dir = android_home / "build-tools"
		var available_versions = build_tools_dir.files

		var build_tools_version
		if available_versions.is_empty then
			print_error "Error: found no Android build-tools, install one or set ANDROID_HOME."
			return
		else
			alpha_comparator.sort available_versions
			build_tools_version = available_versions.last
		end

		# Gather ldflags for Android
		var ldflags = new Array[String]
		var platform_name = "android"
		for mmodule in compiler.mainmodule.in_importation.greaters do
			if mmodule.ldflags.keys.has(platform_name) then
				ldflags.add_all mmodule.ldflags[platform_name]
			end
		end

		# Platform version for OpenGL ES
		var platform_version = ""
		if ldflags.has("-lGLESv3") then
			platform_version = "def platformVersion = 18"
		else if ldflags.has("-lGLESv2") then
			platform_version = "def platformVersion = 12"
		end

		# TODO make configurable client-side
		var compile_sdk_version = app_target_api

		var local_build_gradle = """
apply plugin: 'com.android.application'

{{{platform_version}}}

android {
    compileSdkVersion {{{compile_sdk_version}}}
    buildToolsVersion "{{{build_tools_version}}}"

    defaultConfig {
        applicationId "{{{app_package}}}"
        minSdkVersion {{{app_min_api}}}
        {{{app_max_api}}}
        targetSdkVersion {{{app_target_api}}}
        versionCode {{{project.version_code}}}
        versionName "{{{app_version}}}"
        ndk {
            abiFilters 'armeabi-v7a', 'x86'
        }
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_TOOLCHAIN=gcc"
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }

    lintOptions {
       abortOnError false
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}
"""
		local_build_gradle.write_to_file "{android_project_root}/app/build.gradle"

		# TODO add 'arm64-v8a' and 'x86_64' to `abiFilters` when the min API is available

		# ---
		# Other, smaller files

		# /build.gradle
		var global_build_gradle = """
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}
"""
		global_build_gradle.write_to_file "{android_project_root}/build.gradle"

		# /settings.gradle
		var settings_gradle = """
include ':app'
"""
		settings_gradle.write_to_file "{android_project_root}/settings.gradle"

		# /gradle.properties
		var gradle_properties = """
org.gradle.jvmargs=-Xmx1536m
"""
		gradle_properties.write_to_file "{android_project_root}/gradle.properties"

		# Insert an importation of the generated R class to all Java files from the FFI
		for mod in compiler.mainmodule.in_importation.greaters do
			var java_ffi_file = mod.java_file
			if java_ffi_file != null then java_ffi_file.add "import {app_package}.R;"
		end

		# compile normal C files
		super

		# ---
		# /app/src/main/cpp/CMakeLists.txt

		# Gather extra C files generated elsewhere than in super
		for f in compiler.extern_bodies do
			if f isa ExternCFile then cfiles.add(f.filename.basename)
		end

		# Prepare for the CMakeLists format
		var target_link_libraries = new Array[String]
		for flag in ldflags do
			if flag.has_prefix("-l") then
				target_link_libraries.add flag.substring_from(2)
			end
		end

		# Download the libgc/bdwgc sources
		var share_dir = share_dir
		if not share_dir.file_exists then
			print "Android project error: Nit share directory not found, please use the environment variable NIT_DIR"
			exit 1
		end

		var bdwgc_dir = "{share_dir}/android-bdwgc/bdwgc"
		if not bdwgc_dir.file_exists then
			toolcontext.exec_and_check(["{share_dir}/android-bdwgc/setup.sh"], "Android project error")
		end

		# Compile the native app glue lib if used
		var add_native_app_glue = ""
		if target_link_libraries.has("native_app_glue") then
			add_native_app_glue = """
add_library(native_app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c)
"""
		end

		var cmakelists = """
cmake_minimum_required(VERSION 3.4.1)

{{{add_native_app_glue}}}


# libgc/bdwgc

## The source is in the Nit repo
set(lib_src_DIR {{{bdwgc_dir}}})
set(lib_build_DIR ../libgc/outputs)
file(MAKE_DIRECTORY ${lib_build_DIR})

## Config
add_definitions("-DALL_INTERIOR_POINTERS -DGC_THREADS -DUSE_MMAP -DUSE_MUNMAP -DJAVA_FINALIZATION -DNO_EXECUTE_PERMISSION -DGC_DONT_REGISTER_MAIN_STATIC_DATA")
set(enable_threads TRUE)
set(CMAKE_USE_PTHREADS_INIT TRUE)

## link_map is already defined in Android
add_definitions("-DGC_DONT_DEFINE_LINK_MAP")

## Silence warning
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")

add_subdirectory(${lib_src_DIR} ${lib_build_DIR} )
include_directories(${lib_src_DIR}/include)


# Nit generated code

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DANDROID -DWITH_LIBGC")

# Export ANativeActivity_onCreate(),
# Refer to: https://github.com/android-ndk/ndk/issues/381.
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")

#           name      so       source
add_library(nit_app   SHARED   {{{cfiles.join("\n\t")}}} )

target_include_directories(nit_app PRIVATE ${ANDROID_NDK}/sources/android/native_app_glue)


# Link!

target_link_libraries(nit_app gc-lib
	{{{target_link_libraries.join("\n\t")}}})
"""
		cmakelists.write_to_file "{android_app_main}/cpp/CMakeLists.txt"

		# ---
		# /app/src/main/res/values/strings.xml for app name

		# Set the default pretty application name
		var res_values_dir = "{android_app_main}/res/values/"
		res_values_dir.mkdir
"""<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">{{{app_name}}}</string>
</resources>""".write_to_file res_values_dir/"strings.xml"

		# ---
		# Copy assets, resources in the Android project

		## Collect path to all possible folder where we can find the `android` folder
		var app_files = [project_root]
		app_files.add_all project.files

		for path in app_files do
			# Copy the assets folder
			var assets_dir = path / "assets"
			if assets_dir.file_exists then
				assets_dir = assets_dir.realpath
				toolcontext.exec_and_check(["cp", "-r", assets_dir, android_app_main], "Android project error")
			end

			# Copy the whole `android` folder
			var android_dir = path / "android"
			if android_dir.file_exists then
				android_dir = android_dir.realpath
				for f in android_dir.files do
					toolcontext.exec_and_check(["cp", "-r", android_dir / f, android_app_main], "Android project error")
				end
			end
		end

		# ---
		# Generate AndroidManifest.xml

		# Is there an icon?
		var resolutions = ["ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi", "anydpi", "anydpi-v26"]
		var icon_name = null
		var has_round = false

		for res in resolutions do
			# New style mipmap
			if "{project_root}/android/res/mipmap-{res}/ic_launcher_round.png".file_exists then
				has_round = true
			end
			if "{project_root}/android/res/mipmap-{res}/ic_launcher.png".file_exists then
				icon_name = "@mipmap/ic_launcher"
				break
			end
			if "{project_root}/android/res/mipmap-{res}/ic_launcher.xml".file_exists then
				icon_name = "@mipmap/ic_launcher"
				break
			end
		end
		if icon_name == null then
			# Old style drawable-hdpi/icon.png
			for res in resolutions do
				var path = project_root / "android/res/drawable-{res}/icon.png"
				if path.file_exists then
					icon_name = "@drawable/icon"
					break
				end
			end
		end

		var icon_declaration
		if icon_name != null then
			icon_declaration = "android:icon=\"{icon_name}\""
			if app_target_api >= 25 and has_round then
				icon_declaration += "\n\t\tandroid:roundIcon=\"@mipmap/ic_launcher_round\""
			end
		else icon_declaration = ""

		# TODO android:roundIcon

		# Copy the Java sources files
		var java_dir = android_app_main / "java/"
		java_dir.mkdir
		for mmodule in compiler.mainmodule.in_importation.greaters do
			var extra_java_files = mmodule.extra_java_files
			if extra_java_files != null then for file in extra_java_files do
				var path = file.src_path
				var dir = file.filename.dirname
				(java_dir/dir).mkdir
				path.file_copy_to(java_dir/file.filename)
			end
		end

		# ---
		# /app/src/main/AndroidManifest.xml

		var manifest_file = new FileWriter.open(android_app_main / "AndroidManifest.xml")
		manifest_file.write """
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="{{{app_package}}}">

    <application
		android:hasCode="true"
		android:allowBackup="true"
		android:label="@string/app_name"
		{{{icon_declaration}}}>
"""

		for activity in project.activities do
			manifest_file.write """
        <activity android:name="{{{activity}}}"
                {{{project.manifest_activity_attributes.join("\n")}}}
                {{{icon_declaration}}}>

            <meta-data android:name="android.app.lib_name" android:value="nit_app" />

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
"""
		end

		manifest_file.write """
{{{project.manifest_application_lines.join("\n")}}}

    </application>

{{{project.manifest_lines.join("\n")}}}

</manifest>
"""
		manifest_file.close
	end
src/platform/android.nit:70,2--471,4

nitc $ IOSToolchain :: write_files
	redef fun write_files(compile_dir, cfiles)
	do
		# Clear the project directory before writing anything
		if ios_project_root.file_exists then ios_project_root.rmdir
		compile_dir.mkdir

		# Download the libgc/bdwgc sources
		var nit_dir = toolcontext.nit_dir or else "."
		var share_dir = (nit_dir/"share").realpath
		if not share_dir.file_exists then
			print "iOS project error: Nit share directory not found, please use the environment variable NIT_DIR"
			exit 1
		end

		var bdwgc_dir = "{share_dir}/android-bdwgc/bdwgc"
		self.bdwgc_dir = bdwgc_dir
		if not bdwgc_dir.file_exists then
			toolcontext.exec_and_check(["{share_dir}/android-bdwgc/setup.sh"], "iOS project error")
		end

		super
	end
src/platform/ios.nit:65,2--86,4