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:
Miku AuahDark
2022-11-19 18:55:54 +08:00
parent bd57c5e88f
commit d55d3ce715
4 changed files with 594 additions and 2 deletions
+36 -1
View File
@@ -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 {
+73 -1
View File
@@ -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()
+464
View File
@@ -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.