mirror of
https://github.com/love2d/love-android.git
synced 2026-08-12 00:20:57 +02:00
Implement 3rd-party modules support for new CMake build system.
Existing 3rd-party module with Android.mk will be converted to CMake, with caveats of course. Read lua-modules-readme.txt for more info! Fixes #241.
This commit is contained in:
+36
-1
@@ -18,11 +18,45 @@ android {
|
||||
// Transitive shared library dependency is not taken into account, this
|
||||
// will cause liboboe.so not get included. love depends on OpenAL that
|
||||
// depends on oboe::oboe. So, add "OpenAL" target.
|
||||
targets "love", "OpenAL"
|
||||
targets "love_android"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def retrieveAll3pModules = { ->
|
||||
def modules = []
|
||||
|
||||
fileTree("src/main/cpp/lua-modules/").visit { FileVisitDetails details ->
|
||||
if (details.isDirectory()) {
|
||||
if (file(details.file.path + "/Android.mk").exists() ||
|
||||
file(details.file.path + "/CMakeLists.mk").exists()) {
|
||||
logger.lifecycle("3rd-party module: " + details.file.path)
|
||||
|
||||
def javainfo = file(details.file.path + "/java.txt")
|
||||
if (javainfo.exists()) {
|
||||
def fstream = new FileInputStream(javainfo)
|
||||
def infile = new BufferedReader(new InputStreamReader(fstream))
|
||||
def javapath = infile.readLine().replace("\\", "/")
|
||||
def mpath = null
|
||||
|
||||
if (javapath[0] != '/') {
|
||||
mpath = details.file.path + "/" + javapath
|
||||
} else {
|
||||
mpath = details.file.path + javapath
|
||||
}
|
||||
|
||||
modules << mpath
|
||||
|
||||
logger.lifecycle("Registered path " + mpath)
|
||||
infile.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
@@ -56,6 +90,7 @@ android {
|
||||
srcDir 'src/main/cpp/megasource/libs/SDL2/android-project/app/src/main/java'
|
||||
srcDir 'src/main/java'
|
||||
srcDir 'src/main/cpp/love/src/libraries/luahttps/src/android/java'
|
||||
srcDirs += retrieveAll3pModules()
|
||||
}
|
||||
}
|
||||
normal {
|
||||
|
||||
@@ -1,7 +1,79 @@
|
||||
#
|
||||
# Copyright (c) 2006-2022 LOVE Development Team
|
||||
#
|
||||
# This software is provided 'as-is', without any express or implied
|
||||
# warranty. In no event will the authors be held liable for any damages
|
||||
# arising from the use of this software.
|
||||
#
|
||||
# Permission is granted to anyone to use this software for any purpose,
|
||||
# including commercial applications, and to alter it and redistribute it
|
||||
# freely, subject to the following restrictions:
|
||||
#
|
||||
# 1. The origin of this software must not be misrepresented; you must not
|
||||
# claim that you wrote the original software. If you use this software
|
||||
# in a product, an acknowledgment in the product documentation would be
|
||||
# appreciated but is not required.
|
||||
# 2. Altered source versions must be plainly marked as such, and must not be
|
||||
# misrepresented as being the original software.
|
||||
# 3. This notice may not be removed or altered from any source distribution.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
project(love-android LANGUAGES C CXX)
|
||||
|
||||
set(MEGA_LOVE "${CMAKE_CURRENT_SOURCE_DIR}/love")
|
||||
add_custom_target(love_android)
|
||||
set(LOVE_ANDROID 1)
|
||||
|
||||
# Include Megasource and LOVE
|
||||
set(MEGA_LOVE "${CMAKE_CURRENT_SOURCE_DIR}/love")
|
||||
add_subdirectory(megasource)
|
||||
|
||||
add_dependencies(love_android love OpenAL)
|
||||
|
||||
find_package(Python 3.9 COMPONENTS Interpreter)
|
||||
if(NOT Python_Interpreter_FOUND)
|
||||
message(WARNING "No Python detected, Android.mk won't be converted to CMakeLists.txt!")
|
||||
endif()
|
||||
|
||||
# List lua-modules
|
||||
|
||||
file(GLOB MODULE_LIST
|
||||
LIST_DIRECTORIES TRUE
|
||||
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
CONFIGURE_DEPENDS
|
||||
"lua-modules/*"
|
||||
)
|
||||
foreach(dir ${MODULE_LIST})
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
|
||||
set(has_run_cmake 0)
|
||||
# Load either CMakeLists.txt or convert Android.mk to CMakeLists.txt
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/CMakeLists.txt")
|
||||
add_subdirectory(${dir})
|
||||
set(has_run_cmake 1)
|
||||
elseif(Python_Interpreter_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}/Android.mk")
|
||||
get_filename_component(item_sanitized0 "${dir}" NAME)
|
||||
string(REGEX REPLACE "[^\\w-]" "-" item_sanitized1 "${item_sanitized0}")
|
||||
string(REGEX REPLACE "-+" "-" item "${item_sanitized0}")
|
||||
message(STATUS "Converting ${dir} Android.mk to CMakeLists.txt")
|
||||
execute_process(RESULT_VARIABLE status
|
||||
COMMAND "${Python_EXECUTABLE}" amkparse.py convert "${item}" "${dir}/Android.mk"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
OUTPUT_FILE "${dir}/CMakeLists.txt"
|
||||
)
|
||||
if("${status}" STREQUAL "0")
|
||||
add_subdirectory(${dir})
|
||||
set(has_run_cmake 1)
|
||||
else()
|
||||
message(FATAL_ERROR "amkparse.py returned ${status}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If CMake has been run, retrieve targets and add it as dependency
|
||||
if(has_run_cmake)
|
||||
get_property(defined_build_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
|
||||
get_property(defined_import_targets DIRECTORY "${dir}" PROPERTY IMPORTED_TARGETS)
|
||||
add_dependencies(love_android ${defined_build_targets} ${defined_import_targets})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
P_COMMENTS = re.compile(r"(?<!\\)#.+")
|
||||
P_CONTINUATION = re.compile(r"\s*\\\r?\n\s*")
|
||||
P_EMPTY_LINE = re.compile(r"\s*[\r?\n]+\s*")
|
||||
P_VAR_ASSIGN = re.compile(r"([a-zA-Z_]\w*)\s*:?:?=\"?\s*(.*)\"?")
|
||||
P_MULTI_WHITESPACE = re.compile(r"\s+")
|
||||
P_CLEAN_VARS = re.compile(r"include\s*\$[\(\{]CLEAR_VARS[\)\}]\s*", re.MULTILINE)
|
||||
P_VARIABLE_REF = re.compile(r"\$[\(\{]([a-zA-Z_]\w*)[\)\}]")
|
||||
|
||||
P_BUILD_TYPES = {
|
||||
"executable": re.compile(r"include\s*\$[\(\{]BUILD_EXECUTABLE[\)\}]\s*", re.MULTILINE),
|
||||
"shared": re.compile(r"include\s*\$[\(\{]BUILD_SHARED_LIBRARY[\)\}]\s*", re.MULTILINE),
|
||||
"static": re.compile(r"include\s*\$[\(\{]BUILD_STATIC_LIBRARY[\)\}]\s*", re.MULTILINE),
|
||||
"prebuilt_shared": re.compile(r"include\s*\$[\(\{]PREBUILT_SHARED_LIBRARY[\)\}]\s*", re.MULTILINE),
|
||||
"prebuilt_static": re.compile(r"include\s*\$[\(\{]PREBUILT_STATIC_LIBRARY[\)\}]\s*", re.MULTILINE),
|
||||
}
|
||||
|
||||
substitute_love_with_luajit = True
|
||||
|
||||
|
||||
def transform_makefile(mk: str):
|
||||
# Remove comments
|
||||
mk = re.sub(P_COMMENTS, "", mk)
|
||||
# Remove multiline declaration
|
||||
mk = re.sub(P_CONTINUATION, " ", mk)
|
||||
# Remove multiple newlines
|
||||
mk = re.sub(P_EMPTY_LINE, "\n", mk)
|
||||
mk = re.sub(P_EMPTY_LINE, "\n", mk)
|
||||
# Simplify assignment
|
||||
mk = re.sub(P_VAR_ASSIGN, r"\1=\2", mk)
|
||||
return mk
|
||||
|
||||
|
||||
def list_if_needed(data):
|
||||
if isinstance(data, str):
|
||||
return [data]
|
||||
return data
|
||||
|
||||
|
||||
def str_if_needed(data):
|
||||
if isinstance(data, list):
|
||||
return " ".join(data)
|
||||
return data
|
||||
|
||||
|
||||
def expand_value(value: str, variables: dict[str, str]):
|
||||
def do_replace(x: re.Match[str]):
|
||||
v = x.group(1)
|
||||
return str_if_needed(variables.get(v, f"$({v})"))
|
||||
|
||||
return re.sub(P_VARIABLE_REF, do_replace, value)
|
||||
|
||||
|
||||
def extract_variables(mk: str, variables: dict[str, str] = None):
|
||||
result = {}
|
||||
for match in re.finditer(P_VAR_ASSIGN, mk):
|
||||
value = match.group(2).strip()
|
||||
# Try convert to boolean
|
||||
if value == "true" or value == "false":
|
||||
value = value == "true"
|
||||
# Is it array of strings?
|
||||
elif value.find(" ") >= 0:
|
||||
if variables:
|
||||
# Expand first
|
||||
value = expand_value(value, variables)
|
||||
value = re.sub(P_MULTI_WHITESPACE, " ", value).split(" ")
|
||||
else:
|
||||
try:
|
||||
# Try int conversion
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
# Try float conversion
|
||||
value = float(value)
|
||||
except ValueError:
|
||||
# Fallback default (str)
|
||||
if variables:
|
||||
value = expand_value(value, variables)
|
||||
result[match.group(1)] = value
|
||||
return result
|
||||
|
||||
|
||||
def separate_makefiles(mk: str):
|
||||
# Normalize "include $(CLEAR_VARS)"
|
||||
result = re.sub(P_CLEAN_VARS, "include $(CLEAN_VARS)", mk).split("include $(CLEAN_VARS)")
|
||||
# Transform makefile
|
||||
result = map(transform_makefile, result)
|
||||
# Remove "LOCAL_PATH"
|
||||
def test_filt(x):
|
||||
return x.find("LOCAL_PATH=") == -1
|
||||
|
||||
result = filter(test_filt, result)
|
||||
return list(result)
|
||||
|
||||
|
||||
def extract_variables_with_expand(mk: str, target_vars: dict[str, str]):
|
||||
variables = extract_variables(mk, target_vars)
|
||||
variables = extract_variables(mk, variables | target_vars)
|
||||
return variables
|
||||
|
||||
|
||||
def determine_build_type(mk: str):
|
||||
for name, pattern in P_BUILD_TYPES.items():
|
||||
if re.search(pattern, mk):
|
||||
return name
|
||||
return "unknown"
|
||||
|
||||
|
||||
class CFlagsData:
|
||||
def __init__(self, cflags: str):
|
||||
# Variable setup
|
||||
self.standard = None
|
||||
self.include_paths = []
|
||||
self.features = set()
|
||||
self.defines = set()
|
||||
self.other = []
|
||||
# Parse
|
||||
for flag in cflags.split():
|
||||
if flag.startswith("-std="):
|
||||
# std
|
||||
value = flag[5:].replace("gnu++", "c++")
|
||||
if value == "c99":
|
||||
self.standard = 99
|
||||
elif value == "c++11" or value == "c++0x" or value == "c11":
|
||||
self.standard = 11
|
||||
elif value == "c++14":
|
||||
self.standard = 14
|
||||
elif value == "c++17" or value == "c17":
|
||||
self.standard = 17
|
||||
elif value == "c++2a" or value == "c++20":
|
||||
self.standard = 20
|
||||
else:
|
||||
print(f"Unknown -std: {value}", file=sys.stderr)
|
||||
elif flag.startswith("-I"):
|
||||
# Include paths
|
||||
self.include_paths.append(flag[2:])
|
||||
elif flag.startswith("-f"):
|
||||
# Features
|
||||
self.features.add(flag[2:])
|
||||
elif flag.startswith("-D"):
|
||||
# Defines
|
||||
self.defines.add(flag[2:])
|
||||
else:
|
||||
self.other.append(flag)
|
||||
|
||||
@property
|
||||
def unknowns(self):
|
||||
return " ".join(self.others)
|
||||
|
||||
|
||||
class LDFlagsData:
|
||||
def __init__(self, ldflags: str):
|
||||
# Variable setup
|
||||
self.libraries = set()
|
||||
self.library_paths = []
|
||||
self.other = []
|
||||
# Parse
|
||||
for flag in ldflags.split():
|
||||
if flag.startswith("-fuse-ld="):
|
||||
# linker, ignore
|
||||
pass
|
||||
elif flag.startswith("-l"):
|
||||
# Native, built-in libraries
|
||||
self.libraries.add(flag[2:])
|
||||
elif flag.startswith("-L"):
|
||||
# Library paths
|
||||
self.library_paths.append(flag[2:])
|
||||
else:
|
||||
self.other.append(flag)
|
||||
|
||||
@property
|
||||
def unknowns(self):
|
||||
return " ".join(self.others)
|
||||
|
||||
|
||||
def filter_libraries(name: str):
|
||||
if len(name) < 2:
|
||||
return name
|
||||
if name[0] == "-":
|
||||
if name[1] == "l":
|
||||
return name[2:]
|
||||
else:
|
||||
if name.startswith("lib"):
|
||||
return name[3:]
|
||||
return name
|
||||
return ""
|
||||
|
||||
|
||||
def get_default_output_file(name: str):
|
||||
return name if name.startswith("lib") else ("lib" + name)
|
||||
|
||||
|
||||
class SerializedMakefile:
|
||||
def __init__(self, mk: str, compatsubst: bool = True):
|
||||
variables = extract_variables_with_expand(mk, target_vars)
|
||||
build_type = determine_build_type(mk)
|
||||
# Essential
|
||||
self.name = variables["LOCAL_MODULE"]
|
||||
if self.name.startswith("lib"):
|
||||
self.name = self.name[3:]
|
||||
self.output = variables.get("LOCAL_MODULE_FILENAME", get_default_output_file(self.name))
|
||||
self.type = build_type
|
||||
self.sources = list_if_needed(variables["LOCAL_SRC_FILES"])
|
||||
self.include = list_if_needed(variables.get("LOCAL_C_INCLUDES", []))
|
||||
self.export_include = list_if_needed(variables.get("LOCAL_EXPORT_C_INCLUDES", []))
|
||||
self.allow_undefined = str_if_needed(variables.get("LOCAL_ALLOW_UNDEFINED_SYMBOLS", "")).lower() == "true"
|
||||
self.force_arm_code = str_if_needed(variables.get("LOCAL_ARM_MODE", "")) == "arm"
|
||||
# CFlags
|
||||
cflags = CFlagsData(str_if_needed(variables.get("LOCAL_CFLAGS", "")))
|
||||
export_cflags = CFlagsData(str_if_needed(variables.get("LOCAL_EXPORT_CFLAGS", "")))
|
||||
cppflags = CFlagsData(str_if_needed(variables.get("LOCAL_CPPFLAGS", "")))
|
||||
export_cppflags = CFlagsData(str_if_needed(variables.get("LOCAL_EXPORT_CPPFLAGS", "")))
|
||||
self.include.extend(cflags.include_paths)
|
||||
self.include.extend(cppflags.include_paths)
|
||||
self.export_include.extend(export_cflags.include_paths)
|
||||
self.export_include.extend(export_cppflags.include_paths)
|
||||
# C flags
|
||||
self.c_standard = cflags.standard
|
||||
self.c_compile_features = list(cflags.features)
|
||||
self.c_compile_flags = cflags.other
|
||||
self.export_c_standard = export_cflags.standard
|
||||
self.export_c_compile_features = list(export_cflags.features)
|
||||
self.export_c_compile_flags = export_cflags.other
|
||||
# C++ flags
|
||||
self.cpp_standard = cppflags.standard
|
||||
self.cpp_compile_features = list(cppflags.features)
|
||||
self.cpp_compile_flags = cppflags.other
|
||||
self.export_cpp_standard = export_cppflags.standard
|
||||
self.export_cpp_compile_features = list(export_cppflags.features)
|
||||
self.export_cpp_compile_flags = export_cppflags.other
|
||||
# Linker flags/libraries
|
||||
link_libraries = set()
|
||||
link_libraries.update(
|
||||
filter_libraries(name) for name in list_if_needed(variables.get("LOCAL_SHARED_LIBRARIES", []))
|
||||
)
|
||||
link_libraries.update(
|
||||
filter_libraries(name) for name in list_if_needed(variables.get("LOCAL_STATIC_LIBRARIES", []))
|
||||
)
|
||||
self.link_library_path = []
|
||||
self.link_library_options = []
|
||||
if build_type.find("static") == -1:
|
||||
ldflags = LDFlagsData(str_if_needed(variables.get("LOCAL_LDFLAGS", "")))
|
||||
ldlibs = list_if_needed(variables.get("LOCAL_LDLIBS", []))
|
||||
link_libraries.update(filter_libraries(name) for name in ldlibs)
|
||||
link_libraries.update(filter_libraries(name) for name in ldflags.libraries)
|
||||
self.link_library_path.extend(ldflags.library_paths)
|
||||
self.link_library_options.extend(ldflags.other)
|
||||
self.link_libraries = list(filter(None, link_libraries))
|
||||
export_ldflags = LDFlagsData(str_if_needed(variables.get("LOCAL_EXPORT_LDFLAGS", "")))
|
||||
export_link_libraries = set()
|
||||
export_link_libraries.update(
|
||||
filter_libraries(name) for name in list_if_needed(variables.get("LOCAL_LDLIBS", []))
|
||||
)
|
||||
export_link_libraries.update(filter_libraries(name) for name in export_ldflags.libraries)
|
||||
self.export_link_library_path = export_ldflags.library_paths
|
||||
self.export_link_library_options = export_ldflags.other
|
||||
self.export_link_libraries = list(filter(None, export_link_libraries))
|
||||
if compatsubst:
|
||||
self.link_libraries = list(map(lambda x: "luajit" if x == "love" else x, self.link_libraries))
|
||||
self.export_link_libraries = list(map(lambda x: "luajit" if x == "love" else x, self.export_link_libraries))
|
||||
|
||||
def __iter__(self):
|
||||
yield "name", self.name,
|
||||
yield "output", self.output,
|
||||
yield "type", self.type,
|
||||
yield "allowUndefined", self.allow_undefined
|
||||
yield "forceARMCode", self.force_arm_code
|
||||
yield "sources", self.sources
|
||||
yield "include", self.include
|
||||
yield "exportInclude", self.export_include
|
||||
yield "cStandard", self.c_standard
|
||||
yield "cCompileFeatures", self.c_compile_features
|
||||
yield "cCompileFlags", self.c_compile_flags
|
||||
yield "cppStandard", self.cpp_standard
|
||||
yield "cppCompileFeatures", self.cpp_compile_features
|
||||
yield "cppCompileFlags", self.cpp_compile_flags
|
||||
yield "exportCStandard", self.export_c_standard
|
||||
yield "exportCCompileFeatures", self.export_c_compile_features
|
||||
yield "exportCCompileFlags", self.export_c_compile_flags
|
||||
yield "exportCppStandard", self.export_cpp_standard
|
||||
yield "exportCppCompileFeatures", self.export_cpp_compile_features
|
||||
yield "exportCppCompileFlags", self.export_cpp_compile_flags
|
||||
yield "linkLibraries", self.link_libraries
|
||||
yield "linkLibraryPath", self.link_library_path
|
||||
yield "linkLibraryOptions", self.link_library_options
|
||||
yield "exportLinkLibraries", self.export_link_libraries
|
||||
yield "exportLinkLibraryPath", self.export_link_library_path
|
||||
yield "exportLinkLibraryOptions", self.export_link_library_options
|
||||
|
||||
|
||||
def convert_to_serialized(mk: list[str], target_vars: dict[str, str]):
|
||||
result = [] # type: list[SerializedMakefile]
|
||||
for mks in mk:
|
||||
result.append(SerializedMakefile(mks, substitute_love_with_luajit))
|
||||
return result
|
||||
|
||||
|
||||
def convert_to_json(mk: list[str], target_vars: dict[str, str]):
|
||||
return json.dumps([dict(x) for x in convert_to_serialized(mk, target_vars)])
|
||||
|
||||
|
||||
def write_cmake(stream: io.StringIO, funcname: str, target: str, kind: str = None, props: list[str] = None):
|
||||
stream.write(funcname)
|
||||
stream.write("(")
|
||||
stream.write(target)
|
||||
if kind:
|
||||
stream.write(" ")
|
||||
stream.write(kind)
|
||||
if props:
|
||||
stream.write("\n")
|
||||
for prop in props:
|
||||
stream.write("\t")
|
||||
stream.write(prop)
|
||||
stream.write("\n")
|
||||
stream.write(")\n")
|
||||
|
||||
|
||||
def convert_to_cmake(mk: list[str], target_vars: dict[str, str], project: str):
|
||||
serialized_mk = convert_to_serialized(mk, target_vars)
|
||||
result = io.StringIO()
|
||||
result.write("# Generated by amkparse.py\n")
|
||||
result.write("# Modify if needed then remove this comment.\n")
|
||||
result.write("cmake_minimum_required(VERSION 3.22)\n\n")
|
||||
result.write(f"project({project} LANGUAGES C CXX)\n\n")
|
||||
for s in serialized_mk:
|
||||
result.write(f"#\n# {s.name}\n#\n")
|
||||
if s.type.startswith("prebuilt"):
|
||||
# Prebuilt needs either .so or .a
|
||||
kind = "SHARED" if s.type.find("shared") != -1 else "STATIC"
|
||||
write_cmake(result, "add_library", s.name, f"IMPORTED {kind}")
|
||||
write_cmake(result, "set_target_properties", s.name, "PROPERTIES", [f'IMPORTED_LOCATION "{s.sources[0]}"'])
|
||||
if len(s.export_include) > 0:
|
||||
write_cmake(result, "target_include_directories", s.name, "INTERFACE", s.export_include)
|
||||
else:
|
||||
# add_library/add_executable
|
||||
if s.type == "executable":
|
||||
write_cmake(result, "add_executable", s.name, None, s.sources)
|
||||
else:
|
||||
kind = "SHARED" if s.type.find("shared") != -1 else "STATIC"
|
||||
write_cmake(result, "add_library", s.name, kind, s.sources)
|
||||
# set_target_properties
|
||||
c_standard = s.export_c_standard or s.c_standard
|
||||
cpp_standard = s.export_cpp_standard or s.cpp_standard
|
||||
target_props = [f"OUTPUT_NAME {s.output}", 'PREFIX ""']
|
||||
if c_standard:
|
||||
target_props.append(f"C_STANDARD {c_standard}")
|
||||
if cpp_standard:
|
||||
target_props.append(f"CXX_STANDARD {cpp_standard}")
|
||||
write_cmake(result, "set_target_properties", s.name, "PROPERTIES", target_props)
|
||||
# target_include_directories PRIVATE
|
||||
if len(s.include) > 0:
|
||||
write_cmake(result, "target_include_directories", s.name, "PRIVATE", s.include)
|
||||
# target_include_directories PUBLIC
|
||||
if len(s.export_include) > 0:
|
||||
write_cmake(result, "target_include_directories", s.name, "PUBLIC", s.export_include)
|
||||
# target_link_directories PRIVATE
|
||||
if len(s.link_library_path) > 0:
|
||||
write_cmake(result, "target_link_directories", s.name, "PRIVATE", s.link_library_path)
|
||||
# target_link_directories PUBLIC
|
||||
if len(s.export_link_library_path) > 0:
|
||||
write_cmake(result, "target_link_directories", s.name, "PUBLIC", s.export_link_library_path)
|
||||
# target_link_libraries
|
||||
link_libraries = None
|
||||
export_link_libraries = None
|
||||
if s.type.find("static") != -1:
|
||||
# Merge export and non-export
|
||||
export_link_libraries = set()
|
||||
export_link_libraries.update(s.link_libraries)
|
||||
export_link_libraries.update(s.export_link_libraries)
|
||||
else:
|
||||
link_libraries = s.link_libraries
|
||||
export_link_libraries = s.export_link_libraries
|
||||
if link_libraries:
|
||||
write_cmake(result, "target_link_libraries", s.name, "PRIVATE", link_libraries)
|
||||
if export_link_libraries:
|
||||
write_cmake(result, "target_link_libraries", s.name, "PUBLIC", export_link_libraries)
|
||||
if len(s.c_compile_flags) > 0:
|
||||
print("# TODO: compile flags:", *s.c_compile_flags, file=result)
|
||||
if len(s.c_compile_features) > 0:
|
||||
print("# TODO: compile features:", *s.c_compile_features, file=result)
|
||||
if len(s.cpp_compile_flags) > 0:
|
||||
print("# TODO: compile flags cpp:", *s.cpp_compile_flags, file=result)
|
||||
if len(s.cpp_compile_features) > 0:
|
||||
print("# TODO: compile features cpp:", *s.cpp_compile_features, file=result)
|
||||
if len(s.export_c_compile_flags) > 0:
|
||||
print("# TODO: export compile flags:", *s.export_c_compile_flags, file=result)
|
||||
if len(s.export_c_compile_features) > 0:
|
||||
print("# TODO: export compile features:", *s.export_c_compile_features, file=result)
|
||||
if len(s.export_c_compile_flags) > 0:
|
||||
print("# TODO: export compile flags cpp:", *s.export_c_compile_flags, file=result)
|
||||
if len(s.export_c_compile_features) > 0:
|
||||
print("# TODO: export compile features cpp:", *s.export_c_compile_features, file=result)
|
||||
if len(s.link_library_options) > 0:
|
||||
print("# TODO: link options:", *s.link_library_options, file=result)
|
||||
if len(s.export_link_library_options) > 0:
|
||||
print("# TODO: export link options:", *s.export_link_library_options, file=result)
|
||||
return result.getvalue()
|
||||
|
||||
|
||||
def create_target_vars(path: str, arch: str, api: str):
|
||||
if arch == "cmake":
|
||||
return {
|
||||
"LOCAL_PATH": "${CMAKE_CURRENT_SOURCE_DIR}",
|
||||
"TARGET_ARCH": "${CMAKE_ANDROID_ARCH}",
|
||||
"TARGET_ARCH_ABI": "${CMAKE_ANDROID_ARCH_ABI}",
|
||||
"TARGET_PLATFORM": "android-${CMAKE_SYSTEM_NAME}",
|
||||
"TARGET_ABI": "android-${CMAKE_SYSTEM_NAME}-${CMAKE_ANDROID_ARCH_ABI}",
|
||||
}
|
||||
if arch == "arm64" or arch == "arm64-v8a":
|
||||
arch_pure = "arm64"
|
||||
arch_abi = "arm64-v8a"
|
||||
elif arch == "armeabi-v7a" or arch == "arm":
|
||||
arch_pure = "arm"
|
||||
arch_abi = "armeabi-v7a"
|
||||
else:
|
||||
arch_pure = arch
|
||||
arch_abi = arch
|
||||
return {
|
||||
"LOCAL_PATH": os.path.dirname(os.path.realpath(path)).replace("\\", "/"),
|
||||
"TARGET_ARCH": arch_pure,
|
||||
"TARGET_ARCH_ABI": arch_abi,
|
||||
"TARGET_PLATFORM": f"android-{api}",
|
||||
"TARGET_ABI": f"android-{api}-{arch_abi}",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
subparser = parser.add_subparsers(dest="command", required=True)
|
||||
# CMake converter subparser
|
||||
convert_parser = subparser.add_parser("convert")
|
||||
convert_parser.add_argument("--arch", type=str, help="Target architecture", default="cmake")
|
||||
convert_parser.add_argument("--api", type=int, help="Minimum Android API level", default=0)
|
||||
convert_parser.add_argument("--no-compat", action="store_true", help='Don\'t change "love" to "luajit" target.')
|
||||
convert_parser.add_argument("project", type=str, help="Project name")
|
||||
convert_parser.add_argument("input", type=str, help="Android.mk")
|
||||
# JSON dump subparser
|
||||
dump_parser = subparser.add_parser("dump")
|
||||
dump_parser.add_argument("arch", type=str, help="Target architecture")
|
||||
dump_parser.add_argument("api", type=int, help="Minimum Android API level")
|
||||
dump_parser.add_argument("input", type=str, help="Android.mk")
|
||||
# Parse
|
||||
args = parser.parse_args()
|
||||
# Check command
|
||||
if args.command == "convert":
|
||||
if args.no_compat:
|
||||
substitute_love_with_luajit = False
|
||||
target_vars = create_target_vars(args.input, args.arch, args.api)
|
||||
with open(args.input, "r", encoding="UTF-8") as f:
|
||||
projects = separate_makefiles(f.read())
|
||||
print(convert_to_cmake(projects, target_vars, args.project))
|
||||
elif args.command == "dump":
|
||||
target_vars = create_target_vars(args.input, args.arch, args.api)
|
||||
with open(args.input, "r", encoding="UTF-8") as f:
|
||||
projects = separate_makefiles(f.read())
|
||||
print(convert_to_json(projects, target_vars))
|
||||
@@ -0,0 +1,21 @@
|
||||
This folder should contain external Lua C modules that will be shipped along with LOVE. One for each folder
|
||||
|
||||
Each folder must contains:
|
||||
* Either CMakeLists.txt or Android.mk.
|
||||
* If Android.mk is present:
|
||||
The Android.mk will be converted to CMakeLists.txt. This conversion may not 100% perfect!
|
||||
The LOCAL_MODULE_FILENAME must be set accordingly as ndk-build appends "lib" prefix from LOCAL_MODULE by default.
|
||||
For Lua includes and libraries, simply link the native library with "liblove".
|
||||
Note: This will convert Android.mk to CMakeLists.txt. The conversion may not 100% perfect. Conditionals are not
|
||||
parsed and treated as if all codepaths are taken. Modify the generated CMakeLists.txt if necessary!
|
||||
Caveat: If the library is meant to be loaded as require("my.library") then you MUST set LOCAL_MODULE_FILENAME to
|
||||
my.library (with dots).
|
||||
* If CMakeLists.txt is present:
|
||||
The CMake script is loaded directly.
|
||||
For Lua includes and libraries, link with "luajit" target.
|
||||
Caveat: If the library is meant to be loaded as require("my.library") then you MUST set OUTPUT_NAME property to
|
||||
"my.library" (with dots) and PREFIX property to "" (empty string) on your CMake target using
|
||||
"set_target_properties".
|
||||
* If your module interacts with Java-side, a file java.txt must contain where it should look Java source files.
|
||||
Example, if you have src/java/your/package/name/MyFile.java, then you need to write "src/java" in java.txt.
|
||||
If this file is absent, then your Java-side code will not be compiled along.
|
||||
Reference in New Issue
Block a user