@@ -27,11 +27,17 @@ include(FBCMakeParseArgs)
27
27
# If we fail to find python now we won't fail immediately, but
28
28
# add_fb_python_executable() or add_fb_python_library() will fatal out if they
29
29
# are used.
30
- if (NOT Python3_EXECUTABLE )
30
+ if (NOT TARGET Python3::Interpreter )
31
31
# CMake 3.12+ ships with a FindPython3.cmake module. Try using it first.
32
32
# We find with QUIET here, since otherwise this generates some noisy warnings
33
33
# on versions of CMake before 3.12
34
- find_package (Python3 COMPONENTS Interpreter QUIET )
34
+ if (WIN32 )
35
+ # On Windows we need both the Intepreter as well as the Development
36
+ # libraries.
37
+ find_package (Python3 COMPONENTS Interpreter Development QUIET )
38
+ else ()
39
+ find_package (Python3 COMPONENTS Interpreter QUIET )
40
+ endif ()
35
41
if (Python3_Interpreter_FOUND )
36
42
message (STATUS "Found Python 3: ${Python3_EXECUTABLE} " )
37
43
else ()
@@ -41,10 +47,15 @@ if(NOT Python3_EXECUTABLE)
41
47
if (NOT PYTHONINTERP_FOUND )
42
48
set (Python_ADDITIONAL_VERSIONS 3 3.6 3.5 3.4 3.3 3.2 3.1 )
43
49
find_package (PythonInterp )
50
+ # TODO: On Windows we require the Python libraries as well.
51
+ # We currently do not search for them on this code path.
52
+ # For now we require building with CMake 3.12+ on Windows, so that the
53
+ # FindPython3 code path above is available.
44
54
endif ()
45
55
if (PYTHONINTERP_FOUND )
46
56
if ("${PYTHON_VERSION_MAJOR} " GREATER_EQUAL 3 )
47
57
set (Python3_EXECUTABLE "${PYTHON_EXECUTABLE} " )
58
+ add_custom_target (Python3::Interpreter )
48
59
else ()
49
60
string (
50
61
CONCAT FBPY_FIND_PYTHON_ERR
67
78
FB_PY_TEST_DISCOVER_SCRIPT
68
79
"${CMAKE_CURRENT_LIST_DIR} /FBPythonTestAddTests.cmake"
69
80
)
81
+ set (
82
+ FB_PY_WIN_MAIN_C
83
+ "${CMAKE_CURRENT_LIST_DIR} /fb_py_win_main.c"
84
+ )
70
85
71
86
# An option to control the default installation location for
72
87
# install_fb_python_library(). This is relative to ${CMAKE_INSTALL_PREFIX}
85
100
# run. If left unspecified, a __main__.py script must be present in the
86
101
# manifest.
87
102
#
88
- function (add_fb_python_executable EXE_NAME )
103
+ function (add_fb_python_executable TARGET )
89
104
fb_py_check_available ()
90
105
91
106
# Parse the arguments
@@ -98,7 +113,7 @@ function(add_fb_python_executable EXE_NAME)
98
113
99
114
# Use add_fb_python_library() to perform most of our source handling
100
115
add_fb_python_library (
101
- "${EXE_NAME } .main_lib"
116
+ "${TARGET } .main_lib"
102
117
BASE_DIR "${ARG_BASE_DIR} "
103
118
NAMESPACE "${ARG_NAMESPACE} "
104
119
SOURCES ${ARG_SOURCES}
@@ -107,11 +122,11 @@ function(add_fb_python_executable EXE_NAME)
107
122
108
123
set (
109
124
manifest_files
110
- "$<TARGET_PROPERTY:${EXE_NAME } .main_lib.py_lib,INTERFACE_INCLUDE_DIRECTORIES>"
125
+ "$<TARGET_PROPERTY:${TARGET } .main_lib.py_lib,INTERFACE_INCLUDE_DIRECTORIES>"
111
126
)
112
127
set (
113
128
source_files
114
- "$<TARGET_PROPERTY:${EXE_NAME } .main_lib.py_lib,INTERFACE_SOURCES>"
129
+ "$<TARGET_PROPERTY:${TARGET } .main_lib.py_lib,INTERFACE_SOURCES>"
115
130
)
116
131
117
132
# The command to build the executable archive.
@@ -127,16 +142,25 @@ function(add_fb_python_executable EXE_NAME)
127
142
set (make_py_args --manifest-separator "::" "$<JOIN:${manifest_files} ,::>" )
128
143
endif ()
129
144
130
- set (output_file "${EXE_NAME} " )
145
+ set (output_file "${TARGET}${CMAKE_EXECUTABLE_SUFFIX} " )
146
+ if (WIN32 )
147
+ set (zipapp_output "${TARGET} .py_zipapp" )
148
+ else ()
149
+ set (zipapp_output "${output_file} " )
150
+ endif ()
151
+ set (zipapp_output_file "${zipapp_output} " )
152
+
153
+ set (is_dir_output FALSE )
131
154
if (DEFINED ARG_TYPE )
132
155
list (APPEND make_py_args "--type" "${ARG_TYPE} " )
133
156
if ("${ARG_TYPE} " STREQUAL "dir" )
157
+ set (is_dir_output TRUE )
134
158
# CMake doesn't really seem to like having a directory specified as an
135
159
# output; specify the __main__.py file as the output instead.
136
- set (output_file "${EXE_NAME } /__main__.py" )
160
+ set (zipapp_output_file "${zipapp_output } /__main__.py" )
137
161
list (APPEND
138
162
extra_cmd_params
139
- COMMAND "${CMAKE_COMMAND} " -E remove_directory "${EXE_NAME } "
163
+ COMMAND "${CMAKE_COMMAND} " -E remove_directory "${zipapp_output } "
140
164
)
141
165
endif ()
142
166
endif ()
@@ -146,26 +170,51 @@ function(add_fb_python_executable EXE_NAME)
146
170
endif ()
147
171
148
172
add_custom_command (
149
- OUTPUT "${output_file } "
173
+ OUTPUT "${zipapp_output_file } "
150
174
${extra_cmd_params}
151
175
COMMAND
152
176
"${Python3_EXECUTABLE} " "${FB_MAKE_PYTHON_ARCHIVE} "
153
- -o "${EXE_NAME } "
177
+ -o "${zipapp_output } "
154
178
${make_py_args}
155
179
DEPENDS
156
180
${source_files}
157
- "${EXE_NAME } .main_lib.py_sources_built"
181
+ "${TARGET } .main_lib.py_sources_built"
158
182
"${FB_MAKE_PYTHON_ARCHIVE} "
159
183
)
160
184
161
- # Add an "ALL" target that depends on force ${EXE_NAME},
162
- # so that ${EXE_NAME} will be included in the default list of build targets.
163
- add_custom_target ("${EXE_NAME} .GEN_PY_EXE" ALL DEPENDS "${output_file} " )
185
+ if (WIN32 )
186
+ if (is_dir_output )
187
+ # TODO: generate a main executable that will invoke Python3
188
+ # with the correct main module inside the output directory
189
+ else ()
190
+ add_executable ("${TARGET} .winmain" "${FB_PY_WIN_MAIN_C} " )
191
+ target_link_libraries ("${TARGET} .winmain" Python3::Python )
192
+ # The Python3::Python target doesn't seem to be set up completely
193
+ # correctly on Windows for some reason, and we have to explicitly add
194
+ # ${Python3_LIBRARY_DIRS} to the target link directories.
195
+ target_link_directories (
196
+ "${TARGET} .winmain"
197
+ PUBLIC ${Python3_LIBRARY_DIRS}
198
+ )
199
+ add_custom_command (
200
+ OUTPUT "${output_file} "
201
+ DEPENDS "${TARGET} .winmain" "${zipapp_output_file} "
202
+ COMMAND
203
+ "cmd.exe" "/c" "copy" "/b"
204
+ "${TARGET} .winmain${CMAKE_EXECUTABLE_SUFFIX} +${zipapp_output} "
205
+ "${output_file} "
206
+ )
207
+ endif ()
208
+ endif ()
209
+
210
+ # Add an "ALL" target that depends on force ${TARGET},
211
+ # so that ${TARGET} will be included in the default list of build targets.
212
+ add_custom_target ("${TARGET} .GEN_PY_EXE" ALL DEPENDS "${output_file} " )
164
213
165
214
# Allow resolving the executable path for the target that we generate
166
215
# via a generator expression like:
167
216
# "WATCHMAN_WAIT_PATH=$<TARGET_PROPERTY:watchman-wait.GEN_PY_EXE,EXECUTABLE>"
168
- set_property (TARGET "${EXE_NAME } .GEN_PY_EXE"
217
+ set_property (TARGET "${TARGET } .GEN_PY_EXE"
169
218
PROPERTY EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR} /${output_file} " )
170
219
endfunction ()
171
220
0 commit comments