mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-07-02 14:25:41 +08:00
Compare commits
232 Commits
1.1.1
...
b87a633496
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b87a633496 | ||
|
|
880242ed93 | ||
|
|
3d33c75a05 | ||
|
|
65d38d5efc | ||
|
|
db75b18da4 | ||
|
|
7b8b829c21 | ||
|
|
4eee09c390 | ||
|
|
64054f16c5 | ||
|
|
ae66327e98 | ||
|
|
d00e3d3129 | ||
|
|
df609e28ca | ||
|
|
21c582d309 | ||
|
|
3488bce63d | ||
|
|
5d347adbcf | ||
|
|
36ce06c3ca | ||
|
|
3071297bd5 | ||
|
|
0707bcab68 | ||
|
|
fe7196f2aa | ||
|
|
220252da74 | ||
|
|
65ebe4dfa6 | ||
|
|
580565de23 | ||
|
|
9e8af298d4 | ||
|
|
1ca64d6626 | ||
|
|
1b38f23268 | ||
|
|
1b2ed00cbb | ||
|
|
14a3c28044 | ||
|
|
5767d9a275 | ||
|
|
da802d5e70 | ||
|
|
50b6f5244d | ||
|
|
7f860e46f3 | ||
|
|
5a354d58fa | ||
|
|
f186c4572b | ||
|
|
fb592bf103 | ||
|
|
195c922f4e | ||
|
|
464eca3588 | ||
|
|
0fbef677ca | ||
|
|
99bdb1fec1 | ||
|
|
f6d777a668 | ||
|
|
1515e9d9d5 | ||
|
|
d164b567bf | ||
|
|
38baa371ca | ||
|
|
6c62d032fe | ||
|
|
10541ddcc7 | ||
|
|
f73a02b9a9 | ||
|
|
d755803e90 | ||
|
|
23d8a014ce | ||
|
|
09c5cdae11 | ||
|
|
1b88682fcb | ||
|
|
9cc703d424 | ||
|
|
8fd3cab450 | ||
|
|
b2a4fb115c | ||
|
|
ace647d6c4 | ||
|
|
5048fb5b48 | ||
|
|
4c55f4a22b | ||
|
|
96bc62b2a9 | ||
|
|
c5fc6fa9dc | ||
|
|
ed2b320271 | ||
|
|
1dd4ad9cc1 | ||
|
|
265fac3ca1 | ||
|
|
09437d3e91 | ||
|
|
1f2be3b484 | ||
|
|
87ce77cf25 | ||
|
|
dc5bac99b0 | ||
|
|
6fab3e789d | ||
|
|
b02fa68683 | ||
|
|
a07ba59b0f | ||
|
|
8f978dd187 | ||
|
|
a72ed9ca16 | ||
|
|
4b750825e9 | ||
|
|
7f04a27f41 | ||
|
|
6b3f973a61 | ||
|
|
e3ccf916bf | ||
|
|
de1302c6af | ||
|
|
8055816683 | ||
|
|
7e2b390963 | ||
|
|
fbf4016965 | ||
|
|
2b802c710f | ||
|
|
f7162cbc79 | ||
|
|
347419ee53 | ||
|
|
e4c8815ba7 | ||
|
|
09095f48a7 | ||
|
|
e573f446a5 | ||
|
|
b936730e51 | ||
|
|
6e1c7a09cc | ||
|
|
74060cbf27 | ||
|
|
4cba2cb8c1 | ||
|
|
67ae206b0e | ||
|
|
dfc6694dce | ||
|
|
82170b8d50 | ||
|
|
523aa0a6d3 | ||
|
|
de2a38b5a2 | ||
|
|
212f1de3ae | ||
|
|
6f7f2f75b0 | ||
|
|
7a5612ec2b | ||
|
|
f8a16ce6c0 | ||
|
|
a14d494bba | ||
|
|
e240183fa0 | ||
|
|
d4b6dbfba3 | ||
|
|
0986665d53 | ||
|
|
e6f4c5e41f | ||
|
|
0e9c8ebf61 | ||
|
|
cab1f16734 | ||
|
|
4c56797d14 | ||
|
|
247da4010a | ||
|
|
6f078b6865 | ||
|
|
bfe986e62a | ||
|
|
e3bc589d4b | ||
|
|
b6d1ecf0e5 | ||
|
|
6151fcfda0 | ||
|
|
0c210d088f | ||
|
|
36132012db | ||
|
|
3c96621af4 | ||
|
|
da0b052cc2 | ||
|
|
7fae0f3fe9 | ||
|
|
6970500f85 | ||
|
|
1286a9e237 | ||
|
|
bf085706b0 | ||
|
|
cb1d1cdc5e | ||
|
|
90c6aebac6 | ||
|
|
84ed82d896 | ||
|
|
da6c2a88b1 | ||
|
|
f87f3c2736 | ||
|
|
c41ca40337 | ||
|
|
f9f27045ee | ||
|
|
3496c3a2e8 | ||
|
|
23d7af5a30 | ||
|
|
01a51c58d8 | ||
|
|
be777dc848 | ||
|
|
55f866e5c5 | ||
|
|
94f915d36a | ||
|
|
23aebace18 | ||
|
|
fb2ce8708f | ||
|
|
ac5fb5e763 | ||
|
|
c93fb71709 | ||
|
|
2df9cf2f5f | ||
|
|
7153bd8a57 | ||
|
|
337a8ca820 | ||
|
|
f47c97eb17 | ||
|
|
8dd87eda01 | ||
|
|
cae4e23872 | ||
|
|
8b9251bb3a | ||
|
|
96e25d2ff7 | ||
|
|
7d4e95a742 | ||
|
|
ac35981e0e | ||
|
|
d04cecc733 | ||
|
|
c2876f28a8 | ||
|
|
05f33ded14 | ||
|
|
02c85139b4 | ||
|
|
8ed1945667 | ||
|
|
85c54d46b0 | ||
|
|
31e3788ead | ||
|
|
cd460dd352 | ||
|
|
8667351378 | ||
|
|
e2f5593d91 | ||
|
|
27d255bc40 | ||
|
|
753ae2de39 | ||
| 9c10e4401f | |||
| b57bdaacc6 | |||
| 851b35ea4f | |||
|
|
5c981172de | ||
| 0635341f43 | |||
|
|
65a5cad9cf | ||
|
|
5aa4c0ea67 | ||
|
|
839adb9751 | ||
|
|
f8ab590356 | ||
|
|
282470ad96 | ||
|
|
4488ef397b | ||
|
|
69dd9f124d | ||
|
|
2d3eec300f | ||
|
|
3de37e8e4e | ||
|
|
8e6cd15101 | ||
|
|
2f186e5db9 | ||
|
|
b52c3a5597 | ||
|
|
e29de024d2 | ||
|
|
5f9c65156d | ||
|
|
19b29b4aad | ||
|
|
02b2f14e6d | ||
|
|
8a3df3a2a6 | ||
|
|
5e33b0d84a | ||
|
|
5a47243022 | ||
|
|
d67f765d31 | ||
|
|
294e25bf12 | ||
|
|
0f99625334 | ||
|
|
918546a090 | ||
|
|
1c8e652797 | ||
|
|
66942f866e | ||
|
|
7440fe3936 | ||
|
|
35395538b4 | ||
|
|
36f4833b71 | ||
|
|
33de213a66 | ||
|
|
9fa229fa36 | ||
|
|
6efa11d8af | ||
|
|
cc3fa7d1fd | ||
|
|
de5d92be70 | ||
|
|
cb679247fc | ||
|
|
f7e961c223 | ||
|
|
89fc7c6482 | ||
|
|
12b32ce1a0 | ||
|
|
cdf4d4999e | ||
|
|
6926a5c989 | ||
|
|
2944eb4a75 | ||
|
|
135259e0db | ||
|
|
38e2b587e1 | ||
|
|
92a0078d54 | ||
| e0c7963713 | |||
|
|
1069ae3539 | ||
| 908f7c8b98 | |||
|
|
89a1526501 | ||
|
|
6e9a975e94 | ||
|
|
b3cbf9cc37 | ||
|
|
22ac543fe7 | ||
|
|
cc3aa2ec44 | ||
|
|
609790ea7b | ||
|
|
cb98414cef | ||
|
|
85efd3275b | ||
|
|
2b3f736c68 | ||
|
|
18c74cdadb | ||
|
|
7cb7facec3 | ||
|
|
cf42a4ee71 | ||
|
|
bce1d13d91 | ||
|
|
7ad936f8c2 | ||
|
|
b46b58ad49 | ||
|
|
189dc21f1a | ||
|
|
7bf38281d5 | ||
|
|
980d25c40a | ||
|
|
e1b0013201 | ||
|
|
50e5cea9fb | ||
|
|
6b258bee6b | ||
|
|
ad76951419 | ||
|
|
862f5e9924 | ||
|
|
0a4d604069 | ||
|
|
720341316b |
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
.idea
|
||||
runtime/
|
||||
docker/libraries/
|
||||
docker/extensions/
|
||||
docker/source/
|
||||
|
||||
# Composer file
|
||||
composer.lock
|
||||
/vendor/
|
||||
|
||||
# default source extract directory
|
||||
/source/
|
||||
|
||||
# default source download directory
|
||||
/downloads/
|
||||
|
||||
# default source build root directory
|
||||
/buildroot/
|
||||
|
||||
# php cs fixer cache file
|
||||
.php-cs-fixer.cache
|
||||
69
.php-cs-fixer.php
Normal file
69
.php-cs-fixer.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'@PSR12' => true,
|
||||
'@Symfony' => true,
|
||||
'@PhpCsFixer' => true,
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'list_syntax' => [
|
||||
'syntax' => 'short',
|
||||
],
|
||||
'concat_space' => [
|
||||
'spacing' => 'one',
|
||||
],
|
||||
'blank_line_before_statement' => [
|
||||
'statements' => [
|
||||
'declare',
|
||||
],
|
||||
],
|
||||
'ordered_imports' => [
|
||||
'imports_order' => [
|
||||
'class',
|
||||
'function',
|
||||
'const',
|
||||
],
|
||||
'sort_algorithm' => 'alpha',
|
||||
],
|
||||
'single_line_comment_style' => [
|
||||
'comment_types' => [
|
||||
],
|
||||
],
|
||||
'yoda_style' => [
|
||||
'always_move_variable' => false,
|
||||
'equal' => false,
|
||||
'identical' => false,
|
||||
],
|
||||
'multiline_whitespace_before_semicolons' => [
|
||||
'strategy' => 'no_multi_line',
|
||||
],
|
||||
'constant_case' => [
|
||||
'case' => 'lower',
|
||||
],
|
||||
'class_attributes_separation' => true,
|
||||
'combine_consecutive_unsets' => true,
|
||||
'declare_strict_types' => true,
|
||||
'linebreak_after_opening_tag' => true,
|
||||
'lowercase_static_reference' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_unused_imports' => true,
|
||||
'not_operator_with_successor_space' => false,
|
||||
'not_operator_with_space' => false,
|
||||
'ordered_class_elements' => true,
|
||||
'php_unit_strict' => false,
|
||||
'phpdoc_separation' => false,
|
||||
'single_quote' => true,
|
||||
'standardize_not_equals' => true,
|
||||
'multiline_comment_opening_closing' => true,
|
||||
'phpdoc_summary' => false,
|
||||
'php_unit_test_class_requires_covers' => false,
|
||||
'phpdoc_var_without_name' => false,
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()->in(__DIR__ . '/src')
|
||||
);
|
||||
695
LICENSE
695
LICENSE
@@ -1,674 +1,21 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2023 Jerry Ma
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
69
README-en.md
Executable file
69
README-en.md
Executable file
@@ -0,0 +1,69 @@
|
||||
# static-php-cli
|
||||
Compile A Statically Linked PHP With Swoole and other Extensions.
|
||||
|
||||
Compile a purely static PHP binary file with various extensions to make PHP-cli applications more portable!
|
||||
|
||||
You can also use the micro binary file to package PHP source code and binary files into one for distribution!
|
||||
|
||||
Note: only support cli SAPI, not support fpm, cgi.
|
||||
|
||||
## Compilation Requirements
|
||||
|
||||
Yes, this project is written in PHP, pretty funny.
|
||||
But php-static-cli only requires an environment above PHP 8.0.
|
||||
|
||||
- Linux
|
||||
- Supported arch: aarch64, amd64
|
||||
- Supported distributions: alpine, ubuntu, centos
|
||||
- Requirements: (TODO)
|
||||
- macOS
|
||||
- Supported arch: arm64, x86_64
|
||||
- Requirements: make, bison, flex, pkg-config, git, autoconf, automake, tar, unzip, xz, gzip, bzip2, cmake
|
||||
- Windows
|
||||
- Supported arch: x86_64
|
||||
- Requirements: (TODO)
|
||||
- PHP
|
||||
- Supported version: 8.0, 8.1, 8.2
|
||||
|
||||
## Usage (WIP)
|
||||
|
||||
After stable release for this project, a single phar and single binary for this tool will be published.
|
||||
|
||||
And currently you may need to clone this branch and edit GitHub Action to build.
|
||||
|
||||
```bash
|
||||
chmod +x spc
|
||||
# fetch all libraries
|
||||
./spc fetch --all
|
||||
# with bcmath,openssl,swoole extension, build both CLI and phpmicro SAPI
|
||||
./spc build "bcmath,openssl,swoole" --build-all
|
||||
```
|
||||
|
||||
## Current Status
|
||||
|
||||
- [X] Basic CLI framework (by symfony/console)
|
||||
- [ ] Linux support
|
||||
- [X] macOS support
|
||||
- [X] Exception handler
|
||||
- [ ] Windows support
|
||||
- [ ] PHP 7.4 support
|
||||
|
||||
## Supported Extensions (WIP)
|
||||
|
||||
[Support Extension List](/ext-support.md)
|
||||
|
||||
## Open-Source LICENSE
|
||||
|
||||
This project is based on the tradition of using the MIT License for old versions,
|
||||
while the new version references source code from some other projects.
|
||||
Special thanks to:
|
||||
|
||||
- [dixyes/lwmbs](https://github.com/dixyes/lwmbs) (Mulun Permissive License)
|
||||
- [swoole/swoole-cli](https://github.com/swoole/swoole-cli) (Apache 2.0 LICENSE+SWOOLE-CLI LICENSE)
|
||||
|
||||
Due to the special nature of this project,
|
||||
many other open source projects such as curl and protobuf will be used during the project compilation process,
|
||||
and they all have their own open source licenses.
|
||||
|
||||
Please use the `dump-license`(TODO) command to export the open source licenses used in the project after compilation,
|
||||
and comply with the corresponding project's LICENSE.
|
||||
109
README.md
Normal file → Executable file
109
README.md
Normal file → Executable file
@@ -1,63 +1,66 @@
|
||||
# static-php-swoole
|
||||
Compile A Statically Linked PHP With Swoole and other Extensions
|
||||
编译纯静态的 PHP Binary 二进制文件,带有各种扩展(CLI 模式,暂不支持 CGI 和 FPM 模式)
|
||||
# static-php-cli
|
||||
Compile A Statically Linked PHP With Swoole and other Extensions. [English README](README-en.md)
|
||||
|
||||
[]()
|
||||
编译纯静态的 PHP Binary 二进制文件,带有各种扩展,让 PHP-cli 应用变得更便携!
|
||||
|
||||
## 环境需求
|
||||
- 目前在 x86_64 和 aarch64(arm64) 架构上编译成功,其他架构需自行测试
|
||||
- 需要 Alpine Linux(测试环境为 3.13 版本,其他版本未测试)系统(也就是说需要 musl)
|
||||
- WSL2 也是支持的
|
||||
同时可以使用 micro 二进制文件,将 PHP 源码和 PHP 二进制构建为一个文件分发!
|
||||
|
||||
注:只能编译 CLI 模式,暂不支持 CGI 和 FPM 模式。
|
||||
|
||||
[]()
|
||||
|
||||
## 编译环境需求
|
||||
|
||||
是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。
|
||||
但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 tokenizer 扩展和 PHP 版本大于等于 8.0 即可。
|
||||
|
||||
- Linux
|
||||
- 支持架构: aarch64, amd64
|
||||
- 支持发行版: alpine, ubuntu, centos
|
||||
- 依赖工具: make, bison, flex, pkg-config, git, autoconf, automake, tar, unzip, gzip, bzip2, cmake
|
||||
- macOS
|
||||
- 支持架构: arm64, x86_64
|
||||
- 依赖工具: make, bison, flex, pkg-config, git, autoconf, automake, tar, unzip, xz, gzip, bzip2, cmake
|
||||
- Windows
|
||||
- 支持架构: x86_64
|
||||
- 依赖工具: (TODO)
|
||||
- PHP
|
||||
- 支持版本: 8.0, 8.1, 8.2
|
||||
|
||||
## 使用(WIP)
|
||||
|
||||
> 你正在看的是重构后的 static-php-cli 编译项目,新项目还未完全重构,所以还有大量的扩展没有完成。
|
||||
> 你可以阅读使用 bash 编写的仅为 Linux 系统使用的静态编译脚本和 Docker,详见 bash-version 分支。 旧版本未来将会切换为次要版本,提供有限支持。
|
||||
|
||||
未来会提供一个直接可使用的 phar 包和一个 phpmicro 打包的二进制文件,你可以直接从 Release 中获取并使用:
|
||||
|
||||
## 开始
|
||||
可以直接在旁边的 Release 中下载编译好的二进制,也可以自己编译。
|
||||
```bash
|
||||
# 自己编译
|
||||
./static-compile-php.sh
|
||||
# 完事后在 `php-dist/bin/php` 这个二进制文件可以随意拿着去任何一个 Linux 系统运行了!
|
||||
chmod +x spc
|
||||
# 拉取所有依赖库
|
||||
./spc fetch --all
|
||||
# 构建包含 bcmath,openssl,swoole 扩展的 php-cli 和 micro.sfx
|
||||
./spc build "bcmath,openssl,swoole" --build-all
|
||||
```
|
||||
|
||||
## 包含扩展
|
||||
- bcmath
|
||||
- calendar
|
||||
- ctype
|
||||
- filter
|
||||
- openssl
|
||||
- pcntl
|
||||
- iconv
|
||||
- json
|
||||
- mbstring
|
||||
- phar
|
||||
- curl
|
||||
- pdo
|
||||
- gd
|
||||
- pdo_mysql
|
||||
- mysqlnd
|
||||
- sockets
|
||||
- swoole
|
||||
- redis
|
||||
- simplexml
|
||||
- dom
|
||||
- xml
|
||||
- xmlwriter
|
||||
- xmlreader
|
||||
- posix
|
||||
- tokenizer
|
||||
## 项目支持情况(WIP)
|
||||
|
||||
## 运行示例
|
||||
编译后的状态
|
||||

|
||||
- [X] 基础结构编写(采用 symfony/console`)
|
||||
- [X] 错误处理
|
||||
- [X] macOS 支持
|
||||
- [ ] Windows 支持
|
||||
- [ ] Linux 支持
|
||||
- [ ] PHP 7.4 支持
|
||||
|
||||
在不同系统直接运行 Swoft
|
||||

|
||||
## 支持的扩展情况(WIP)
|
||||
|
||||
## Todo List
|
||||
- [X] curl/libcurl 扩展静态编译
|
||||
- [ ] 可自行选择不需要编译进入的扩展
|
||||
- [ ] php.ini 内嵌或分发
|
||||
- [ ] 尝试带进去个 composer(其实自己下完全可以)
|
||||
[扩展支持列表](/ext-support.md)
|
||||
|
||||
## 参考资料
|
||||
- <https://blog.terrywh.net/post/2019/php-static-openssl/>
|
||||
- <https://stackoverflow.com/a/37245653>
|
||||
- <http://blog.gaoyuan.xyz/2014/04/09/statically-compile-php/>
|
||||
## 开源协议
|
||||
|
||||
本项目依据旧版本惯例采用 MIT License 开源,新版本采用了部分项目的源代码做参考,特别感谢:
|
||||
|
||||
- [dixyes/lwmbs](https://github.com/dixyes/lwmbs)(木兰宽松许可证)
|
||||
- [swoole/swoole-cli](https://github.com/swoole/swoole-cli)(Apache 2.0 LICENSE+SWOOLE-CLI LICENSE)
|
||||
|
||||
因本项目的特殊性,使用项目编译过程中会使用很多其他开源项目,例如 curl、protobuf 等,它们都有各自的开源协议。
|
||||
请在编译完成后,使用命令 `dump-license`(TODO) 导出项目使用项目的开源协议,并遵守对应项目的 LICENSE。
|
||||
|
||||
16
bin/spc
Executable file
16
bin/spc
Executable file
@@ -0,0 +1,16 @@
|
||||
#!php
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 防止 Micro 打包状态下不支持中文的显示(虽然这个项目目前好像没输出过中文?)
|
||||
if (PHP_OS_FAMILY === 'Windows' && Phar::running()) {
|
||||
exec('CHCP 65001');
|
||||
}
|
||||
|
||||
// 跑,反正一条命令跑就对了
|
||||
try {
|
||||
(new \SPC\ConsoleApplication())->run();
|
||||
} catch (Exception $e) {
|
||||
\SPC\exception\ExceptionHandler::getInstance()->handle($e);
|
||||
}
|
||||
44
captainhook.json
Normal file
44
captainhook.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"pre-push": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer analyse"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pre-commit": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer cs-fix -- --config=.php-cs-fixer.php --dry-run --diff {$STAGED_FILES|of-type:php}",
|
||||
"conditions": [
|
||||
{
|
||||
"exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType",
|
||||
"args": ["php"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"post-change": {
|
||||
"enabled": true,
|
||||
"actions": [
|
||||
{
|
||||
"action": "composer install",
|
||||
"options": [],
|
||||
"conditions": [
|
||||
{
|
||||
"exec": "\\CaptainHook\\App\\Hook\\Condition\\FileChanged\\Any",
|
||||
"args": [
|
||||
[
|
||||
"composer.json",
|
||||
"composer.lock"
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
44
composer.json
Normal file
44
composer.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"require": {
|
||||
"php": ">= 8.0",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-iconv": "*",
|
||||
"symfony/console": "^6 || ^5 || ^4",
|
||||
"zhamao/logger": "^1.0",
|
||||
"crazywhalecc/cli-helper": "^0.1.0",
|
||||
"nunomaduro/collision": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2 != 3.7.0",
|
||||
"phpstan/phpstan": "^1.1",
|
||||
"captainhook/captainhook": "^5.10",
|
||||
"captainhook/plugin-composer": "^5.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SPC\\": "src/SPC"
|
||||
},
|
||||
"files": [
|
||||
"src/globals/defines.php",
|
||||
"src/globals/functions.php"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
},
|
||||
"bin": [
|
||||
"bin/spc"
|
||||
],
|
||||
"scripts": {
|
||||
"analyse": "phpstan analyse --memory-limit 300M",
|
||||
"cs-fix": "php-cs-fixer fix",
|
||||
"test": "bin/phpunit --no-coverage"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"phpstan/extension-installer": true,
|
||||
"captainhook/plugin-composer": true
|
||||
},
|
||||
"optimize-autoloader": true,
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
387
config/ext.json
Normal file
387
config/ext.json
Normal file
@@ -0,0 +1,387 @@
|
||||
{
|
||||
"bcmath": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"bz2": {
|
||||
"type": "builtin",
|
||||
"arg-type": "custom",
|
||||
"lib-depends": [
|
||||
"bzip2"
|
||||
]
|
||||
},
|
||||
"calendar": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"ctype": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"curl": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"curl"
|
||||
]
|
||||
},
|
||||
"dba": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with"
|
||||
},
|
||||
"dom": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"enchant": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"enchant2"
|
||||
]
|
||||
},
|
||||
"exif": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"ffi": {
|
||||
"arg-type": "with",
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"libffi"
|
||||
]
|
||||
},
|
||||
"fileinfo": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"filter": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"ftp": {
|
||||
"type": "builtin",
|
||||
"lib-suggests": [
|
||||
"openssl"
|
||||
]
|
||||
},
|
||||
"gd": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"zlib",
|
||||
"libpng"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"gd",
|
||||
"libavif",
|
||||
"libwebp",
|
||||
"libjpeg",
|
||||
"xpm",
|
||||
"libfreetype"
|
||||
],
|
||||
"lib-depends-windows": [
|
||||
"libiconv",
|
||||
"libfreetype",
|
||||
"libjpeg",
|
||||
"zlib",
|
||||
"libpng",
|
||||
"xpm"
|
||||
],
|
||||
"lib-suggests-windows": [
|
||||
"gd",
|
||||
"libavif",
|
||||
"libwebp"
|
||||
]
|
||||
},
|
||||
"gettext": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"gettext"
|
||||
]
|
||||
},
|
||||
"gmp": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"gmp"
|
||||
]
|
||||
},
|
||||
"iconv": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends-windows": [
|
||||
"libiconv"
|
||||
]
|
||||
},
|
||||
"imap": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"imap"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"kerberos"
|
||||
]
|
||||
},
|
||||
"intl": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"icu"
|
||||
]
|
||||
},
|
||||
"ldap": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"ldap"
|
||||
]
|
||||
},
|
||||
"mbregex": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"onig"
|
||||
]
|
||||
},
|
||||
"mbstring": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"onig"
|
||||
]
|
||||
},
|
||||
"mysqli": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"ext-depends": [
|
||||
"mysqlnd"
|
||||
]
|
||||
},
|
||||
"mysqlnd": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with"
|
||||
},
|
||||
"opcache": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"openssl": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"openssl"
|
||||
]
|
||||
},
|
||||
"pcntl": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
},
|
||||
"pdo": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"pdo_mysql": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"ext-depends": [
|
||||
"pdo",
|
||||
"mysqlnd"
|
||||
]
|
||||
},
|
||||
"pdo_pgsql": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"ext-depends": [
|
||||
"pdo"
|
||||
],
|
||||
"lib-depends": [
|
||||
"pq"
|
||||
]
|
||||
},
|
||||
"pdo_sqlite": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"ext-depends": [
|
||||
"pdo",
|
||||
"sqlite3"
|
||||
],
|
||||
"lib-depends": [
|
||||
"sqlite"
|
||||
]
|
||||
},
|
||||
"phar": {
|
||||
"type": "builtin",
|
||||
"lib-suggests": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"posix": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
},
|
||||
"pspell": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"aspell"
|
||||
]
|
||||
},
|
||||
"readline": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"readline"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"libedit",
|
||||
"ncurses"
|
||||
]
|
||||
},
|
||||
"redis": {
|
||||
"type": "external",
|
||||
"source": "redis"
|
||||
},
|
||||
"session": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"shmop": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"simplexml": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"snmp": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"net-snmp"
|
||||
]
|
||||
},
|
||||
"soap": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"sockets": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"sodium": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"sodium"
|
||||
]
|
||||
},
|
||||
"sqlite3": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"sqlite"
|
||||
]
|
||||
},
|
||||
"swoole": {
|
||||
"type": "external",
|
||||
"source": "swoole",
|
||||
"lib-depends": [
|
||||
"openssl",
|
||||
"curl"
|
||||
],
|
||||
"ext-depends": [
|
||||
"openssl"
|
||||
],
|
||||
"ext-suggests": [
|
||||
"curl"
|
||||
],
|
||||
"unix-only": true
|
||||
},
|
||||
"swow": {
|
||||
"type": "external",
|
||||
"source": "swow",
|
||||
"lib-suggests": [
|
||||
"openssl",
|
||||
"curl"
|
||||
],
|
||||
"ext-suggests": [
|
||||
"curl"
|
||||
]
|
||||
},
|
||||
"sysvmsg": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
},
|
||||
"sysvsem": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
},
|
||||
"sysvshm": {
|
||||
"type": "builtin",
|
||||
"unix-only": true
|
||||
},
|
||||
"tidy": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"tidy"
|
||||
]
|
||||
},
|
||||
"tokenizer": {
|
||||
"type": "builtin"
|
||||
},
|
||||
"xml": {
|
||||
"type": "builtin",
|
||||
"arg-type-windows": "with",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"xmlreader": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"xmlwriter": {
|
||||
"type": "builtin",
|
||||
"lib-depends": [
|
||||
"libxml2"
|
||||
]
|
||||
},
|
||||
"xsl": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"libxslt"
|
||||
]
|
||||
},
|
||||
"yaml": {
|
||||
"type": "external",
|
||||
"source": "yaml",
|
||||
"arg-type": "with",
|
||||
"lib-depends": [
|
||||
"libyaml"
|
||||
]
|
||||
},
|
||||
"zip": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"arg-type-windows": "enable",
|
||||
"lib-depends": [
|
||||
"libzip"
|
||||
]
|
||||
},
|
||||
"zlib": {
|
||||
"type": "builtin",
|
||||
"arg-type": "with",
|
||||
"arg-type-windows": "enable",
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"zstd": {
|
||||
"type": "external",
|
||||
"source": "ext-zstd",
|
||||
"lib-depends": [
|
||||
"zstd"
|
||||
]
|
||||
}
|
||||
}
|
||||
330
config/lib.json
Normal file
330
config/lib.json
Normal file
@@ -0,0 +1,330 @@
|
||||
{
|
||||
"brotli": {
|
||||
"source": "brotli",
|
||||
"static-libs-unix": [
|
||||
"libbrotlidec-static.a",
|
||||
"libbrotlienc-static.a",
|
||||
"libbrotlicommon-static.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"brotlicommon-static.lib",
|
||||
"brotlienc-static.lib",
|
||||
"brotlidec-static.lib"
|
||||
],
|
||||
"headers": [
|
||||
"brotli"
|
||||
]
|
||||
},
|
||||
"bzip2": {
|
||||
"source": "bzip2",
|
||||
"static-libs-unix": [
|
||||
"libbz2.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"libbz2.lib",
|
||||
"libbz2_a.lib"
|
||||
]
|
||||
],
|
||||
"headers": [
|
||||
"bzlib.h"
|
||||
]
|
||||
},
|
||||
"curl": {
|
||||
"source": "curl",
|
||||
"static-libs-unix": [
|
||||
"libcurl.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libcurl.lib"
|
||||
],
|
||||
"headers": [
|
||||
"curl"
|
||||
],
|
||||
"lib-depends-unix": [
|
||||
"zlib"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"libssh2",
|
||||
"brotli",
|
||||
"nghttp2",
|
||||
"zstd",
|
||||
"openssl",
|
||||
"idn2",
|
||||
"psl"
|
||||
],
|
||||
"lib-suggests-windows": [
|
||||
"zlib",
|
||||
"libssh2",
|
||||
"brotli",
|
||||
"nghttp2",
|
||||
"zstd",
|
||||
"openssl",
|
||||
"idn2",
|
||||
"psl"
|
||||
],
|
||||
"frameworks": [
|
||||
"CoreFoundation",
|
||||
"SystemConfiguration"
|
||||
]
|
||||
},
|
||||
"libffi": {
|
||||
"source": "libffi",
|
||||
"static-libs-unix": [
|
||||
"libffi.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libffi.lib"
|
||||
],
|
||||
"headers-unix": [
|
||||
"ffi.h",
|
||||
"ffitarget.h"
|
||||
],
|
||||
"headers-windows": [
|
||||
"ffi.h",
|
||||
"fficonfig.h",
|
||||
"ffitarget.h"
|
||||
]
|
||||
},
|
||||
"libpng": {
|
||||
"source": "libpng",
|
||||
"static-libs-unix": [
|
||||
"libpng.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libpng16_static.lib"
|
||||
],
|
||||
"headers-unix": [
|
||||
"png.h",
|
||||
"pngconf.h",
|
||||
"pnglibconf.h"
|
||||
],
|
||||
"headers-windows": [
|
||||
"png.h",
|
||||
"pngconf.h"
|
||||
],
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"libssh2": {
|
||||
"source": "libssh2",
|
||||
"static-libs-unix": [
|
||||
"libssh2.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libssh2.lib"
|
||||
],
|
||||
"headers": [
|
||||
"libssh2.h",
|
||||
"libssh2_publickey.h",
|
||||
"libssh2_sftp.h"
|
||||
],
|
||||
"lib-depends": [
|
||||
"openssl"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"libxml2": {
|
||||
"source": "libxml2",
|
||||
"static-libs-unix": [
|
||||
"libxml2.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"libxml2s.lib",
|
||||
"libxml2_a.lib"
|
||||
]
|
||||
],
|
||||
"headers": [
|
||||
"libxml2"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"icu",
|
||||
"xz",
|
||||
"zlib"
|
||||
],
|
||||
"lib-suggests-windows": [
|
||||
"icu",
|
||||
"xz",
|
||||
"zlib",
|
||||
"pthreads4w"
|
||||
]
|
||||
},
|
||||
"libyaml": {
|
||||
"source": "libyaml",
|
||||
"static-libs-unix": [
|
||||
"libyaml.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"yaml.lib"
|
||||
],
|
||||
"headers": [
|
||||
"yaml.h"
|
||||
]
|
||||
},
|
||||
"libzip": {
|
||||
"source": "libzip",
|
||||
"static-libs-unix": [
|
||||
"libzip.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"zip.lib",
|
||||
"libzip_a.lib"
|
||||
]
|
||||
],
|
||||
"headers": [
|
||||
"zip.h",
|
||||
"zipconf.h"
|
||||
],
|
||||
"lib-depends": [
|
||||
"zlib"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"bzip2",
|
||||
"xz",
|
||||
"zstd",
|
||||
"openssl"
|
||||
]
|
||||
},
|
||||
"nghttp2": {
|
||||
"source": "nghttp2",
|
||||
"static-libs-unix": [
|
||||
"libnghttp2.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"nghttp2.lib"
|
||||
],
|
||||
"headers": [
|
||||
"nghttp2"
|
||||
],
|
||||
"lib-depends": [
|
||||
"zlib",
|
||||
"openssl"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"libxml2",
|
||||
"libev",
|
||||
"libcares",
|
||||
"libngtcp2",
|
||||
"libnghttp3",
|
||||
"libbpf",
|
||||
"libevent-openssl",
|
||||
"jansson",
|
||||
"jemalloc",
|
||||
"systemd",
|
||||
"cunit"
|
||||
]
|
||||
},
|
||||
"onig": {
|
||||
"source": "onig",
|
||||
"static-libs-unix": [
|
||||
"libonig.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"onig.lib",
|
||||
"onig_a.lib"
|
||||
]
|
||||
],
|
||||
"headers": [
|
||||
"oniggnu.h",
|
||||
"oniguruma.h"
|
||||
]
|
||||
},
|
||||
"openssl": {
|
||||
"source": "openssl",
|
||||
"static-libs-unix": [
|
||||
"libssl.a",
|
||||
"libcrypto.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"libssl.lib",
|
||||
"libcrypto.lib"
|
||||
],
|
||||
"headers": [
|
||||
"openssl"
|
||||
],
|
||||
"lib-suggests": [
|
||||
"zlib"
|
||||
]
|
||||
},
|
||||
"pthreads4w": {
|
||||
"source": "pthreads4w",
|
||||
"static-libs-windows": [
|
||||
"libpthreadVC3.lib"
|
||||
],
|
||||
"headers-windows": [
|
||||
"_ptw32.h",
|
||||
"pthread.h",
|
||||
"sched.h",
|
||||
"semaphore.h"
|
||||
]
|
||||
},
|
||||
"sqlite": {
|
||||
"source": "sqlite",
|
||||
"static-libs-unix": [
|
||||
"libsqlite3.a"
|
||||
],
|
||||
"headers-unix": [
|
||||
"sqlite3.h",
|
||||
"sqlite3ext.h"
|
||||
]
|
||||
},
|
||||
"xz": {
|
||||
"source": "xz",
|
||||
"static-libs-unix": [
|
||||
"liblzma.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"liblzma.lib",
|
||||
"liblzma_a.lib"
|
||||
]
|
||||
],
|
||||
"headers-unix": [
|
||||
"lzma"
|
||||
],
|
||||
"headers-windows": [
|
||||
"lzma",
|
||||
"lzma.h"
|
||||
]
|
||||
},
|
||||
"zlib": {
|
||||
"source": "zlib",
|
||||
"static-libs-unix": [
|
||||
"libz.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
"zlib_a.lib"
|
||||
],
|
||||
"headers": [
|
||||
"zlib.h",
|
||||
"zconf.h"
|
||||
]
|
||||
},
|
||||
"zstd": {
|
||||
"source": "zstd",
|
||||
"static-libs-unix": [
|
||||
"libzstd.a"
|
||||
],
|
||||
"static-libs-windows": [
|
||||
[
|
||||
"zstd.lib",
|
||||
"zstd_static.lib"
|
||||
]
|
||||
],
|
||||
"headers-unix": [
|
||||
"zdict.h",
|
||||
"zstd.h",
|
||||
"zstd_errors.h"
|
||||
],
|
||||
"headers-windows": [
|
||||
"zstd.h",
|
||||
"zstd_errors.h"
|
||||
]
|
||||
}
|
||||
}
|
||||
220
config/source.json
Normal file
220
config/source.json
Normal file
File diff suppressed because one or more lines are too long
59
ext-support.md
Normal file
59
ext-support.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Extension List
|
||||
|
||||
> - yes: supported and tested
|
||||
> - untested: supported but not tested
|
||||
> - empty: not supported yet
|
||||
> - issue link: not supported yet due to issue
|
||||
|
||||
| | Linux | macOS | Windows |
|
||||
|------------|-------|----------|---------|
|
||||
| bcmath | | yes | |
|
||||
| bz2 | | untested | |
|
||||
| calendar | | yes | |
|
||||
| ctype | | | |
|
||||
| curl | | yes | |
|
||||
| date | | yes | |
|
||||
| dom | | | |
|
||||
| event | | | |
|
||||
| exif | | | |
|
||||
| filter | | | |
|
||||
| fileinfo | | | |
|
||||
| ftp | | | |
|
||||
| gd | | untested | |
|
||||
| gmp | | untested | |
|
||||
| hash | | yes | |
|
||||
| iconv | | | |
|
||||
| inotify | | | |
|
||||
| json | | yes | |
|
||||
| libxml | | | |
|
||||
| mbstring | | | |
|
||||
| mcrypt | | | |
|
||||
| mongodb | | | |
|
||||
| mysqli | | | |
|
||||
| mysqlnd | | | |
|
||||
| openssl | | yes | |
|
||||
| pcntl | | untested | |
|
||||
| pcre | | yes | |
|
||||
| pdo | | yes | |
|
||||
| pdo_mysql | | | |
|
||||
| pdo_sqlite | | yes | |
|
||||
| pdo_pgsql | | | |
|
||||
| phar | | | |
|
||||
| posix | | | |
|
||||
| protobuf | | | |
|
||||
| readline | | | |
|
||||
| redis | | | |
|
||||
| Reflection | | yes | |
|
||||
| shmop | | | |
|
||||
| simplexml | | | |
|
||||
| soap | | | |
|
||||
| sockets | | | |
|
||||
| sqlite3 | | untested | |
|
||||
| swow | | | |
|
||||
| swoole | | yes | |
|
||||
| tokenizer | | | |
|
||||
| xml | | | |
|
||||
| xmlreader | | | |
|
||||
| xmlwriter | | | |
|
||||
| zip | | | |
|
||||
| zlib | | | |
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
_main_dir="$(pwd)/"
|
||||
|
||||
_build_dir="$_main_dir/build"
|
||||
|
||||
mkdir "$_build_dir" > /dev/null 2>&1
|
||||
|
||||
for loop in "7.4.16" "8.0.3"
|
||||
do
|
||||
sed -i 's/_php_ver=.*/_php_ver="'$loop'"/g' "$_main_dir""static-compile-php.sh" && \
|
||||
rm -rf "$_main_dir""source/php.tar.gz" "$_main_dir""source/php-*" "$_main_dir""php-dist" && \
|
||||
"$_main_dir""static-compile-php.sh" && \
|
||||
cp "$_main_dir""php-dist/bin/php" "$_build_dir/" && \
|
||||
cd "$_build_dir/" && \
|
||||
tar -zcvf "php-$loop-static-bin-"$(uname -m)".tar.gz" "./php" && \
|
||||
mv "./php" "./php-$loop" && \
|
||||
cd "$_main_dir"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Compile static php-$loop failed!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
12
phpstan.neon
Normal file
12
phpstan.neon
Normal file
@@ -0,0 +1,12 @@
|
||||
parameters:
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
level: 4
|
||||
paths:
|
||||
- ./src/
|
||||
ignoreErrors:
|
||||
- '#Constant .* not found#'
|
||||
- '#Unsafe usage of new static#'
|
||||
- '#class Fiber#'
|
||||
- '#Attribute class JetBrains\\PhpStorm\\ArrayShape does not exist#'
|
||||
dynamicConstantNames:
|
||||
- PHP_OS_FAMILY
|
||||
52
src/SPC/ConsoleApplication.php
Normal file
52
src/SPC/ConsoleApplication.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC;
|
||||
|
||||
use SPC\command\DeployCommand;
|
||||
use SPC\store\FileSystem;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Command\HelpCommand;
|
||||
use Symfony\Component\Console\Command\ListCommand;
|
||||
|
||||
/**
|
||||
* spc 应用究级入口
|
||||
*/
|
||||
class ConsoleApplication extends Application
|
||||
{
|
||||
public const VERSION = '2.0-alpha1';
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException
|
||||
* @throws exception\FileSystemException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('static-php-cli', self::VERSION);
|
||||
|
||||
global $argv;
|
||||
|
||||
// 生产环境不显示详细的调试错误,只使用 symfony console 自带的错误显示
|
||||
$this->setCatchExceptions(file_exists(ROOT_DIR . '/.prod') || !in_array('--debug', $argv));
|
||||
|
||||
// 通过扫描目录 src/static-php-cli/command/ 添加子命令
|
||||
$commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command');
|
||||
$this->addCommands(array_map(function ($x) { return new $x(); }, array_filter($commands, function ($y) {
|
||||
if (is_a($y, DeployCommand::class, true) && (class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar'))) {
|
||||
return false;
|
||||
}
|
||||
$reflection = new \ReflectionClass($y);
|
||||
return !$reflection->isAbstract() && !$reflection->isInterface();
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载以去除一些不必要的默认命令
|
||||
*/
|
||||
protected function getDefaultCommands(): array
|
||||
{
|
||||
return [new HelpCommand(), new ListCommand()];
|
||||
}
|
||||
}
|
||||
227
src/SPC/builder/BuilderBase.php
Normal file
227
src/SPC/builder/BuilderBase.php
Normal file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\DependencyUtil;
|
||||
|
||||
abstract class BuilderBase
|
||||
{
|
||||
/** @var bool 是否启用 ZTS 线程安全 */
|
||||
public bool $zts = false;
|
||||
|
||||
/** @var string 编译目标架构 */
|
||||
public string $arch;
|
||||
|
||||
/** @var string GNU 格式的编译目标架构 */
|
||||
public string $gnu_arch;
|
||||
|
||||
/** @var int 编译进程数 */
|
||||
public int $concurrency = 1;
|
||||
|
||||
/** @var array<string, LibraryBase> 要编译的 libs 列表 */
|
||||
protected array $libs = [];
|
||||
|
||||
/** @var array<string, Extension> 要编译的扩展列表 */
|
||||
protected array $exts = [];
|
||||
|
||||
/** @var bool 本次编译是否只编译 libs,不编译 PHP */
|
||||
protected bool $libs_only = false;
|
||||
|
||||
/**
|
||||
* 构建指定列表的 libs
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function buildLibs(array $libraries): void
|
||||
{
|
||||
// 通过扫描目录查找 lib
|
||||
$support_lib_list = [];
|
||||
$classes = FileSystem::getClassesPsr4(
|
||||
ROOT_DIR . '/src/SPC/builder/' . osfamily2dir() . '/library',
|
||||
'SPC\\builder\\' . osfamily2dir() . '\\library'
|
||||
);
|
||||
foreach ($classes as $class) {
|
||||
if (defined($class . '::NAME') && $class::NAME !== 'unknown' && Config::getLib($class::NAME) !== null) {
|
||||
$support_lib_list[$class::NAME] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果传入了空,则默认检查和安置所有支持的lib,libraries为要build的,support_lib_list为支持的列表
|
||||
if ($libraries === [] && $this->isLibsOnly()) {
|
||||
$libraries = array_keys($support_lib_list);
|
||||
}
|
||||
|
||||
// 排序 libs,根据依赖计算一个新的列表出来
|
||||
$libraries = DependencyUtil::getLibsByDeps($libraries);
|
||||
|
||||
// 这里筛选 libraries,比如纯静态模式排除掉ffi
|
||||
if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) {
|
||||
$k = array_search('libffi', $libraries, true);
|
||||
$k !== false && array_splice($libraries, $k, 1);
|
||||
}
|
||||
|
||||
// 过滤不支持的库后添加
|
||||
foreach ($libraries as $library) {
|
||||
if (!isset($support_lib_list[$library])) {
|
||||
throw new RuntimeException('library [' . $library . '] is in the lib.json list but not supported to compile, but in the future I will support it!');
|
||||
}
|
||||
$lib = new ($support_lib_list[$library])($this);
|
||||
$this->addLib($lib);
|
||||
}
|
||||
|
||||
// 统计还没 fetch 到本地的库
|
||||
$this->checkLibsSource();
|
||||
|
||||
// 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的
|
||||
foreach ($this->libs as $lib) {
|
||||
$lib->calcDependency();
|
||||
}
|
||||
foreach ($this->libs as $lib) {
|
||||
match ($lib->tryBuild()) {
|
||||
BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'),
|
||||
BUILD_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'),
|
||||
BUILD_STATUS_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'),
|
||||
default => logger()->warning('lib [' . $lib::NAME . '] build status unknown'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加要编译的 Lib 库
|
||||
*
|
||||
* @param LibraryBase $library Lib 库对象
|
||||
*/
|
||||
public function addLib(LibraryBase $library): void
|
||||
{
|
||||
$this->libs[$library::NAME] = $library;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取要编译的 Lib 库对象
|
||||
*
|
||||
* @param string $name 库名称
|
||||
*/
|
||||
public function getLib(string $name): ?LibraryBase
|
||||
{
|
||||
return $this->libs[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加要编译的扩展
|
||||
*
|
||||
* @param Extension $extension 扩展对象
|
||||
*/
|
||||
public function addExt(Extension $extension): void
|
||||
{
|
||||
$this->exts[$extension->getName()] = $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取要编译的扩展对象
|
||||
*
|
||||
* @param string $name 扩展名称
|
||||
*/
|
||||
public function getExt(string $name): ?Extension
|
||||
{
|
||||
return $this->exts[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本次 Builder 是否为仅编译库的模式
|
||||
*/
|
||||
public function setLibsOnly(bool $status = true): void
|
||||
{
|
||||
$this->libs_only = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检验 ext 扩展列表是否合理,并声明 Extension 对象,检查扩展的依赖
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function proveExts(array $extensions): void
|
||||
{
|
||||
if (defined('BUILD_ALL_STATIC') && BUILD_ALL_STATIC) {
|
||||
$k = array_search('ffi', $extensions, true);
|
||||
$k !== false && array_splice($extensions, $k, 1);
|
||||
}
|
||||
foreach ($extensions as $extension) {
|
||||
$ext = new Extension($extension, $this);
|
||||
$this->addExt($ext);
|
||||
}
|
||||
|
||||
foreach ($this->exts as $ext) {
|
||||
// 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理
|
||||
$ext->checkDependency();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始构建 PHP
|
||||
* 构建 micro 的规则:
|
||||
* - BUILD_MICRO_NONE(默认):只编译 cli
|
||||
* - BUILD_MICRO_ONLY:只编译 micro
|
||||
* - BUILD_MICRO_BOTH:同时编译 micro 和 cli
|
||||
*
|
||||
* @param int $build_micro_rule 规则
|
||||
* @param bool $with_clean 是否为新构建?
|
||||
* @param bool $bloat 保留
|
||||
*/
|
||||
abstract public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false);
|
||||
|
||||
/**
|
||||
* 生成依赖的扩展编译启用参数
|
||||
* 例如 --enable-mbstring 等
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function makeExtensionArgs(): string
|
||||
{
|
||||
$ret = [];
|
||||
foreach ($this->exts as $ext) {
|
||||
$ret[] = $ext->getConfigureArg();
|
||||
}
|
||||
logger()->info('Using configure: ' . implode(' ', $ret));
|
||||
return implode(' ', $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回是否只编译 libs 的模式
|
||||
*/
|
||||
public function isLibsOnly(): bool
|
||||
{
|
||||
return $this->libs_only;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function checkLibsSource(): void
|
||||
{
|
||||
$not_downloaded = [];
|
||||
foreach ($this->libs as $lib) {
|
||||
if (!file_exists($lib->getSourceDir())) {
|
||||
$not_downloaded[] = $lib::NAME;
|
||||
}
|
||||
}
|
||||
if ($not_downloaded !== []) {
|
||||
throw new RuntimeException(
|
||||
'"' . implode(', ', $not_downloaded) .
|
||||
'" totally ' . count($not_downloaded) .
|
||||
' source' . (count($not_downloaded) === 1 ? '' : 's') .
|
||||
' not downloaded, maybe you need to "fetch" ' . (count($not_downloaded) === 1 ? 'it' : 'them') . ' first?'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/SPC/builder/BuilderProvider.php
Normal file
42
src/SPC/builder/BuilderProvider.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use SPC\builder\linux\LinuxBuilder;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\builder\windows\WindowsBuilder;
|
||||
use SPC\exception\RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* 用于生成对应系统环境的 Builder 对象的类
|
||||
*/
|
||||
class BuilderProvider
|
||||
{
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function makeBuilderByInput(InputInterface $input): BuilderBase
|
||||
{
|
||||
return match (PHP_OS_FAMILY) {
|
||||
// 'Windows' => new WindowsBuilder(
|
||||
// binary_sdk_dir: $input->getOption('with-sdk-binary-dir'),
|
||||
// vs_ver: $input->getOption('vs-ver'),
|
||||
// arch: $input->getOption('arch'),
|
||||
// ),
|
||||
'Darwin' => new MacOSBuilder(
|
||||
cc: $input->getOption('cc'),
|
||||
cxx: $input->getOption('cxx'),
|
||||
arch: $input->getOption('arch'),
|
||||
),
|
||||
// 'Linux' => new LinuxBuilder(
|
||||
// cc: $input->getOption('cc'),
|
||||
// cxx: $input->getOption('cxx'),
|
||||
// arch: $input->getOption('arch'),
|
||||
// ),
|
||||
default => throw new RuntimeException('Current OS is not supported yet'),
|
||||
};
|
||||
}
|
||||
}
|
||||
299
src/SPC/builder/Extension.php
Normal file
299
src/SPC/builder/Extension.php
Normal file
@@ -0,0 +1,299 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
|
||||
class Extension
|
||||
{
|
||||
protected array $dependencies = [];
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function __construct(protected string $name, protected BuilderBase $builder)
|
||||
{
|
||||
$ext_type = Config::getExt($this->name, 'type');
|
||||
$unix_only = Config::getExt($this->name, 'unix-only', false);
|
||||
$windows_only = Config::getExt($this->name, 'windows-only', false);
|
||||
if (PHP_OS_FAMILY !== 'Windows' && $windows_only) {
|
||||
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Linux and macOS platform");
|
||||
}
|
||||
if (PHP_OS_FAMILY === 'Windows' && $unix_only) {
|
||||
throw new RuntimeException("{$ext_type} extension {$name} is not supported on Windows platform");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取开启该扩展的 PHP 编译添加的参数
|
||||
*
|
||||
* @throws FileSystemException|RuntimeException
|
||||
*/
|
||||
public function getConfigureArg(): string
|
||||
{
|
||||
$arg = $this->getEnableArg();
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
$arg .= $this->getWindowsConfigureArg();
|
||||
break;
|
||||
case 'Darwin':
|
||||
case 'Linux':
|
||||
$arg .= $this->getUnixConfigureArg();
|
||||
break;
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ext 的 arg-type 获取对应开启的参数,一般都是 --enable-xxx 和 --with-xxx
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getEnableArg(): string
|
||||
{
|
||||
$_name = str_replace('_', '-', $this->name);
|
||||
return match ($arg_type = Config::getExt($this->name, 'arg-type', 'enable')) {
|
||||
'enable' => '--enable-' . $_name,
|
||||
'with' => '--with-' . $_name,
|
||||
'none', 'custom' => '',
|
||||
default => throw new RuntimeException("argType does not accept {$arg_type}, use [enable/with] ."),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出当前扩展依赖的所有 lib 库生成的 .a 静态编译库文件,以字符串形式导出,用空格分割
|
||||
*/
|
||||
public function getLibFilesString(): string
|
||||
{
|
||||
$ret = array_map(
|
||||
fn ($x) => $x->getStaticLibFiles(),
|
||||
$this->getLibraryDependencies(recursive: true)
|
||||
);
|
||||
return implode(' ', $ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function checkDependency(): static
|
||||
{
|
||||
foreach (Config::getExt($this->name, 'lib-depends', []) as $name) {
|
||||
$this->addLibraryDependency($name);
|
||||
}
|
||||
foreach (Config::getExt($this->name, 'lib-suggests', []) as $name) {
|
||||
$this->addLibraryDependency($name, true);
|
||||
}
|
||||
foreach (Config::getExt($this->name, 'ext-depends', []) as $name) {
|
||||
$this->addExtensionDependency($name);
|
||||
}
|
||||
foreach (Config::getExt($this->name, 'ext-suggests', []) as $name) {
|
||||
$this->addExtensionDependency($name, true);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExtensionDependency(): array
|
||||
{
|
||||
return array_filter($this->dependencies, fn ($x) => $x instanceof Extension);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function addLibraryDependency(string $name, bool $optional = false): void
|
||||
{
|
||||
$depLib = $this->builder->getLib($name);
|
||||
if (!$depLib) {
|
||||
if (!$optional) {
|
||||
throw new RuntimeException("extension {$this->name} requires library {$name}");
|
||||
}
|
||||
logger()->info("enabling {$this->name} without library {$name}");
|
||||
} else {
|
||||
$this->dependencies[] = $depLib;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function addExtensionDependency(string $name, bool $optional = false): void
|
||||
{
|
||||
$depExt = $this->builder->getExt($name);
|
||||
if (!$depExt) {
|
||||
if (!$optional) {
|
||||
throw new RuntimeException("{$this->name} requires extension {$name}");
|
||||
}
|
||||
logger()->info("enabling {$this->name} without extension {$name}");
|
||||
} else {
|
||||
$this->dependencies[] = $depExt;
|
||||
}
|
||||
}
|
||||
|
||||
private function getWindowsConfigureArg(): string
|
||||
{
|
||||
$arg = '';
|
||||
switch ($this->name) {
|
||||
case 'redis':
|
||||
// $arg = '--enable-redis';
|
||||
// if ($this->builder->getLib('zstd')) {
|
||||
// $arg .= ' --enable-redis-zstd --with-libzstd ';
|
||||
// }
|
||||
break;
|
||||
case 'xml':
|
||||
case 'soap':
|
||||
case 'xmlreader':
|
||||
case 'xmlwriter':
|
||||
case 'dom':
|
||||
$arg .= ' --with-libxml ';
|
||||
break;
|
||||
case 'swow':
|
||||
if ($this->builder->getLib('openssl')) {
|
||||
$arg .= ' --enable-swow-ssl';
|
||||
}
|
||||
if ($this->builder->getLib('curl')) {
|
||||
$arg .= ' --enable-swow-curl';
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
private function getUnixConfigureArg(): string
|
||||
{
|
||||
$arg = '';
|
||||
switch ($this->name) {
|
||||
/*case 'event':
|
||||
$arg = ' --with-event-core --with-event-libevent-dir="' . BUILD_ROOT_PATH . '"';
|
||||
if ($this->builder->getLib('openssl')) {
|
||||
$arg .= ' --with-event-openssl --with-openssl-dir="' . BUILD_ROOT_PATH . '"';
|
||||
}
|
||||
break;*/
|
||||
case 'sqlite3':
|
||||
$arg = ' --with-sqlite3="' . BUILD_ROOT_PATH . '" ' .
|
||||
'SQLITE_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'SQLITE_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'redis':
|
||||
$arg = ' --enable-redis --disable-redis-session';
|
||||
if ($this->builder->getLib('zstd')) {
|
||||
$arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '" ';
|
||||
}
|
||||
break;
|
||||
case 'yaml':
|
||||
$arg .= ' --with-yaml="' . BUILD_ROOT_PATH . '" ';
|
||||
break;
|
||||
case 'zstd':
|
||||
$arg .= ' --with-libzstd';
|
||||
break;
|
||||
case 'bz2':
|
||||
$arg = ' --with-bz2="' . BUILD_ROOT_PATH . '" ';
|
||||
break;
|
||||
case 'openssl':
|
||||
$arg .= ' ' .
|
||||
'OPENSSL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'OPENSSL_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'curl':
|
||||
$arg .= ' ' .
|
||||
'CURL_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'CURL_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'gd':
|
||||
$arg .= ' ' .
|
||||
'PNG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'PNG_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
// TODO: other libraries
|
||||
case 'phar':
|
||||
case 'zlib':
|
||||
$arg .= ' ' .
|
||||
'ZLIB_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'ZLIB_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'xml': // xml may use expat
|
||||
if ($this->getLibraryDependencies()['expat'] ?? null) {
|
||||
$arg .= ' --with-expat="' . BUILD_ROOT_PATH . '" ' .
|
||||
'EXPAT_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'EXPAT_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
}
|
||||
// no break
|
||||
case 'soap':
|
||||
case 'xmlreader':
|
||||
case 'xmlwriter':
|
||||
case 'dom':
|
||||
$arg .= ' --with-libxml="' . BUILD_ROOT_PATH . '" ' .
|
||||
'LIBXML_CFLAGS=-I"' . realpath('include/libxml2') . '" ' .
|
||||
'LIBXML_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'ffi':
|
||||
$arg .= ' ' .
|
||||
'FFI_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'FFI_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'zip':
|
||||
$arg .= ' ' .
|
||||
'LIBZIP_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'LIBZIP_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'mbregex':
|
||||
$arg .= ' ' .
|
||||
'ONIG_CFLAGS=-I"' . BUILD_INCLUDE_PATH . '" ' .
|
||||
'ONIG_LIBS="' . $this->getLibFilesString() . '" ';
|
||||
break;
|
||||
case 'swow':
|
||||
$arg .= $this->builder->getLib('openssl') ? ' --enable-swow-ssl' : ' --disable-swow-ssl';
|
||||
$arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl';
|
||||
break;
|
||||
case 'swoole':
|
||||
if ($this->builder->getLib('openssl')) {
|
||||
$arg .= ' --enable-openssl';
|
||||
} else {
|
||||
$arg .= ' --disable-openssl --without-openssl';
|
||||
}
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
private function getLibraryDependencies(bool $recursive = false): array
|
||||
{
|
||||
$ret = array_filter($this->dependencies, fn ($x) => $x instanceof LibraryBase);
|
||||
if (!$recursive) {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
$deps = [];
|
||||
|
||||
$added = 1;
|
||||
while ($added !== 0) {
|
||||
$added = 0;
|
||||
foreach ($ret as $depName => $dep) {
|
||||
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
|
||||
if (!in_array($depdepName, array_keys($deps), true)) {
|
||||
$deps[$depdepName] = $depdep;
|
||||
++$added;
|
||||
}
|
||||
}
|
||||
if (!in_array($depName, array_keys($deps), true)) {
|
||||
$deps[$depName] = $dep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $deps;
|
||||
}
|
||||
}
|
||||
190
src/SPC/builder/LibraryBase.php
Normal file
190
src/SPC/builder/LibraryBase.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* Lib 库的基类操作对象
|
||||
*/
|
||||
abstract class LibraryBase
|
||||
{
|
||||
/** @var string lib 依赖名称,必须重写 */
|
||||
public const NAME = 'unknown';
|
||||
|
||||
/** @var string lib 依赖的根目录 */
|
||||
protected string $source_dir;
|
||||
|
||||
/** @var array 依赖列表 */
|
||||
protected array $dependencies = [];
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct(?string $source_dir = null)
|
||||
{
|
||||
if (static::NAME === 'unknown') {
|
||||
throw new RuntimeException('no unknown!!!!!');
|
||||
}
|
||||
$this->source_dir = $source_dir ?? (SOURCE_PATH . '/' . static::NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 lib 库的根目录
|
||||
*/
|
||||
public function getSourceDir(): string
|
||||
{
|
||||
return $this->source_dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 lib 库的所有依赖列表
|
||||
*
|
||||
* @param bool $recursive 是否递归获取(默认为 False)
|
||||
* @return array<string, LibraryBase> 依赖的 Map
|
||||
*/
|
||||
public function getDependencies(bool $recursive = false): array
|
||||
{
|
||||
// 非递归情况下直接返回通过 addLibraryDependency 方法添加的依赖
|
||||
if (!$recursive) {
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
// 下面为递归获取依赖列表,根据依赖顺序
|
||||
$deps = [];
|
||||
|
||||
$added = 1;
|
||||
while ($added !== 0) {
|
||||
$added = 0;
|
||||
foreach ($this->dependencies as $depName => $dep) {
|
||||
foreach ($dep->getDependencies(true) as $depdepName => $depdep) {
|
||||
if (!in_array($depdepName, array_keys($deps), true)) {
|
||||
$deps[$depdepName] = $depdep;
|
||||
++$added;
|
||||
}
|
||||
}
|
||||
if (!in_array($depName, array_keys($deps), true)) {
|
||||
$deps[$depName] = $dep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算依赖列表,不符合依赖将抛出异常
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function calcDependency(): void
|
||||
{
|
||||
// 先从配置文件添加依赖,这里根据不同的操作系统分别选择不同的元信息
|
||||
/*
|
||||
选择规则:
|
||||
如果是 Windows 系统,则依次尝试有无 lib-depends-windows、lib-depends-win、lib-depends。
|
||||
如果是 macOS 系统,则依次尝试 lib-depends-darwin、lib-depends-unix、lib-depends。
|
||||
如果是 Linux 系统,则依次尝试 lib-depends-linux、lib-depends-unix、lib-depends。
|
||||
*/
|
||||
foreach (Config::getLib(static::NAME, 'lib-depends', []) as $dep_name) {
|
||||
$this->addLibraryDependency($dep_name);
|
||||
}
|
||||
foreach (Config::getLib(static::NAME, 'lib-suggests', []) as $dep_name) {
|
||||
$this->addLibraryDependency($dep_name, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前库编译出来获取到的静态库文件列表
|
||||
*
|
||||
* @return string[] 获取编译出来后的需要的静态库文件列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getStaticLibs(): array
|
||||
{
|
||||
return Config::getLib(static::NAME, 'static-libs', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 lib 编译出来的 C Header 文件列表
|
||||
*
|
||||
* @return string[] 获取编译出来后需要的 C Header 文件列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return Config::getLib(static::NAME, 'headers', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 证明该库是否已编译好且就绪,如果没有就绪,内部会调用 build 来进行构建该库
|
||||
*
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function tryBuild(bool $force_build = false): int
|
||||
{
|
||||
// 传入 true,表明直接编译
|
||||
if ($force_build) {
|
||||
$this->build();
|
||||
return BUILD_STATUS_OK;
|
||||
}
|
||||
|
||||
// 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态
|
||||
foreach ($this->getStaticLibs() as $name) {
|
||||
if (!file_exists(BUILD_LIB_PATH . "/{$name}")) {
|
||||
$this->tryBuild(true);
|
||||
return BUILD_STATUS_OK;
|
||||
}
|
||||
}
|
||||
// 头文件同理
|
||||
foreach ($this->getHeaders() as $name) {
|
||||
if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) {
|
||||
$this->tryBuild(true);
|
||||
return BUILD_STATUS_OK;
|
||||
}
|
||||
}
|
||||
// 到这里说明所有的文件都存在,就跳过编译
|
||||
return BUILD_STATUS_ALREADY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取构建当前 lib 的 Builder 对象
|
||||
*/
|
||||
abstract public function getBuilder(): BuilderBase;
|
||||
|
||||
/**
|
||||
* 构建该库需要调用的命令和操作
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
abstract protected function build();
|
||||
|
||||
/**
|
||||
* 添加 lib 库的依赖库
|
||||
*
|
||||
* @param string $name 依赖名称
|
||||
* @param bool $optional 是否是可选依赖(默认为 False)
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function addLibraryDependency(string $name, bool $optional = false): void
|
||||
{
|
||||
// Log::i("add $name as dep of {$this->name}");
|
||||
$dep_lib = $this->getBuilder()->getLib($name);
|
||||
if (!$dep_lib) {
|
||||
if (!$optional) {
|
||||
throw new RuntimeException(static::NAME . " requires library {$name}");
|
||||
}
|
||||
logger()->debug('enabling ' . static::NAME . " without {$name}");
|
||||
} else {
|
||||
$this->dependencies[$name] = $dep_lib;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/SPC/builder/LibraryInterface.php
Normal file
10
src/SPC/builder/LibraryInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder;
|
||||
|
||||
interface LibraryInterface
|
||||
{
|
||||
public function getName(): string;
|
||||
}
|
||||
263
src/SPC/builder/macos/MacOSBuilder.php
Normal file
263
src/SPC/builder/macos/MacOSBuilder.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\macos\library\MacOSLibraryBase;
|
||||
use SPC\builder\traits\UnixBuilderTrait;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\Patcher;
|
||||
|
||||
/**
|
||||
* macOS 系统环境下的构建器
|
||||
* 源于 Config,但因为感觉叫 Config 不太合适,就换成了 Builder
|
||||
*/
|
||||
class MacOSBuilder extends BuilderBase
|
||||
{
|
||||
/** 编译的 Unix 工具集 */
|
||||
use UnixBuilderTrait;
|
||||
|
||||
/** @var string[] MacOS 环境下编译依赖的命令 */
|
||||
public const REQUIRED_COMMANDS = ['make', 'bison', 'flex', 'pkg-config', 'git', 'autoconf', 'automake', 'tar', 'unzip', 'xz', 'gzip', 'bzip2', 'cmake'];
|
||||
|
||||
/** @var bool 标记是否 patch 了 phar */
|
||||
private bool $phar_patched = false;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __construct(?string $cc = null, ?string $cxx = null, ?string $arch = null)
|
||||
{
|
||||
// 如果是 Debug 模式,才使用 set -x 显示每条执行的命令
|
||||
$this->set_x = defined('DEBUG_MODE') ? 'set -x' : 'true';
|
||||
// 初始化一些默认参数
|
||||
$this->cc = $cc ?? 'clang';
|
||||
$this->cxx = $cxx ?? 'clang++';
|
||||
$this->arch = $arch ?? php_uname('m');
|
||||
$this->gnu_arch = arch2gnu($this->arch);
|
||||
// 根据 CPU 线程数设置编译进程数
|
||||
$this->concurrency = SystemUtil::getCpuCount();
|
||||
// 设置 cflags
|
||||
$this->arch_c_flags = SystemUtil::getArchCFlags($this->arch);
|
||||
$this->arch_cxx_flags = SystemUtil::getArchCFlags($this->arch);
|
||||
// 设置 cmake
|
||||
$this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->arch, $this->arch_c_flags);
|
||||
// 设置 configure 依赖的环境变量
|
||||
$this->configure_env =
|
||||
'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' .
|
||||
"CC='{$this->cc}' " .
|
||||
"CXX='{$this->cxx}' " .
|
||||
"CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration'";
|
||||
// 保存丢失的命令
|
||||
$missing = [];
|
||||
foreach (self::REQUIRED_COMMANDS as $cmd) {
|
||||
if (SystemUtil::findCommand($cmd) === null) {
|
||||
$missing[] = $cmd;
|
||||
}
|
||||
}
|
||||
if (!empty($missing)) {
|
||||
throw new RuntimeException('missing system commands: ' . implode(', ', $missing));
|
||||
}
|
||||
|
||||
// 创立 pkg-config 和放头文件的目录
|
||||
f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true);
|
||||
f_mkdir(BUILD_INCLUDE_PATH, recursive: true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成库构建采用的 autoconf 参数列表
|
||||
*
|
||||
* @param string $name 要构建的 lib 库名,传入仅供输出日志
|
||||
* @param array $lib_specs 依赖的 lib 库的 autoconf 文件
|
||||
*/
|
||||
public function makeAutoconfArgs(string $name, array $lib_specs): string
|
||||
{
|
||||
$ret = '';
|
||||
foreach ($lib_specs as $libName => $arr) {
|
||||
$lib = $this->getLib($libName);
|
||||
|
||||
$arr = $arr ?? [];
|
||||
|
||||
$disableArgs = $arr[0] ?? null;
|
||||
$prefix = $arr[1] ?? null;
|
||||
if ($lib instanceof MacOSLibraryBase) {
|
||||
logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support");
|
||||
$ret .= $lib->makeAutoconfEnv($prefix) . ' ';
|
||||
} else {
|
||||
logger()->info("{$name} \033[31;1mwithout\033[0;1m {$libName} support");
|
||||
$ret .= ($disableArgs ?? "--with-{$libName}=no") . ' ';
|
||||
}
|
||||
}
|
||||
return rtrim($ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回 macOS 系统依赖的框架列表
|
||||
*
|
||||
* @param bool $asString 是否以字符串形式返回(默认为 False)
|
||||
*/
|
||||
public function getFrameworks(bool $asString = false): array|string
|
||||
{
|
||||
$libs = [];
|
||||
|
||||
// reorder libs
|
||||
foreach ($this->libs as $lib) {
|
||||
foreach ($lib->getDependencies() as $dep) {
|
||||
$libs[] = $dep;
|
||||
}
|
||||
$libs[] = $lib;
|
||||
}
|
||||
|
||||
$frameworks = [];
|
||||
/** @var MacOSLibraryBase $lib */
|
||||
foreach ($libs as $lib) {
|
||||
array_push($frameworks, ...$lib->getFrameworks());
|
||||
}
|
||||
|
||||
if ($asString) {
|
||||
return implode(' ', array_map(fn ($x) => "-framework {$x}", $frameworks));
|
||||
}
|
||||
return $frameworks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function buildPHP(int $build_micro_rule = BUILD_MICRO_NONE, bool $with_clean = false, bool $bloat = false): void
|
||||
{
|
||||
$extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') ? '-lc++ ' : '');
|
||||
if (!$bloat) {
|
||||
$extra_libs .= implode(' ', $this->getAllStaticLibFiles());
|
||||
} else {
|
||||
logger()->info('bloat linking');
|
||||
$extra_libs .= implode(
|
||||
' ',
|
||||
array_map(
|
||||
fn ($x) => "-Wl,-force_load,{$x}",
|
||||
array_filter($this->getAllStaticLibFiles())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// patch before configure
|
||||
Patcher::patchPHPBeforeConfigure($this);
|
||||
|
||||
f_passthru(
|
||||
$this->set_x . ' && ' .
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
'./buildconf --force'
|
||||
);
|
||||
|
||||
Patcher::patchPHPConfigure($this);
|
||||
|
||||
if ($this->getLib('libxml2') || $this->getExt('iconv')) {
|
||||
$extra_libs .= ' -liconv';
|
||||
}
|
||||
|
||||
f_passthru(
|
||||
$this->set_x . ' && ' .
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
'./configure ' .
|
||||
'--prefix= ' .
|
||||
'--with-valgrind=no ' . // 不检测内存泄漏
|
||||
'--enable-shared=no ' .
|
||||
'--enable-static=yes ' .
|
||||
"--host={$this->gnu_arch}-apple-darwin " .
|
||||
"CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " .
|
||||
'--disable-all ' .
|
||||
'--disable-cgi ' .
|
||||
'--disable-phpdbg ' .
|
||||
'--enable-cli ' .
|
||||
'--enable-micro ' .
|
||||
($this->zts ? '--enable-zts' : '') . ' ' .
|
||||
$this->makeExtensionArgs() . ' ' .
|
||||
$this->configure_env
|
||||
);
|
||||
|
||||
if ($with_clean) {
|
||||
logger()->info('cleaning up');
|
||||
f_passthru(
|
||||
$this->set_x . ' && ' .
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
'make clean'
|
||||
);
|
||||
}
|
||||
|
||||
switch ($build_micro_rule) {
|
||||
case BUILD_MICRO_NONE:
|
||||
logger()->info('building cli');
|
||||
$this->buildCli($extra_libs);
|
||||
break;
|
||||
case BUILD_MICRO_ONLY:
|
||||
logger()->info('building micro');
|
||||
$this->buildMicro($extra_libs);
|
||||
break;
|
||||
case BUILD_MICRO_BOTH:
|
||||
logger()->info('building cli and micro');
|
||||
$this->buildCli($extra_libs);
|
||||
$this->buildMicro($extra_libs);
|
||||
break;
|
||||
}
|
||||
|
||||
if (php_uname('m') === $this->arch) {
|
||||
$this->sanityCheck($build_micro_rule);
|
||||
}
|
||||
|
||||
if ($this->phar_patched) {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 -R < sapi/micro/patches/phar.patch');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 phpmicro
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function buildMicro(string $extra_libs): void
|
||||
{
|
||||
if ($this->getExt('phar')) {
|
||||
$this->phar_patched = true;
|
||||
try {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src && patch -p1 < sapi/micro/patches/phar.patch');
|
||||
} catch (RuntimeException $e) {
|
||||
logger()->error('failed to patch phat due to patch exit with code ' . $e->getCode());
|
||||
$this->phar_patched = false;
|
||||
}
|
||||
}
|
||||
|
||||
f_passthru(
|
||||
$this->set_x . ' && ' .
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
"make -j{$this->concurrency} " .
|
||||
'EXTRA_CFLAGS="-g -Os -fno-ident" ' .
|
||||
"EXTRA_LIBS=\"{$extra_libs} -lresolv\" " .
|
||||
'STRIP="dsymutil -f " ' .
|
||||
// TODO: comment things
|
||||
'micro'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 cli
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function buildCli(string $extra_libs): void
|
||||
{
|
||||
f_passthru(
|
||||
$this->set_x . ' && ' .
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
"make -j{$this->concurrency} " .
|
||||
'EXTRA_CFLAGS="-g -Os -fno-ident" ' . // 生成调试信息、优化编译后的尺寸、禁用标识符(如变量、函数名)缩短
|
||||
"EXTRA_LIBS=\"{$extra_libs} -lresolv -lsqlite3\" " .
|
||||
// TODO: comment things
|
||||
'cli &&' .
|
||||
'dsymutil -f sapi/cli/php &&' .
|
||||
'strip sapi/cli/php'
|
||||
);
|
||||
}
|
||||
}
|
||||
43
src/SPC/builder/macos/SystemUtil.php
Normal file
43
src/SPC/builder/macos/SystemUtil.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos;
|
||||
|
||||
use SPC\builder\traits\UnixSystemUtilTrait;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class SystemUtil
|
||||
{
|
||||
/** macOS 兼容 unix 的系统工具 */
|
||||
use UnixSystemUtilTrait;
|
||||
|
||||
/**
|
||||
* 获取系统 CPU 逻辑内核数
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getCpuCount(): int
|
||||
{
|
||||
f_exec('sysctl -n hw.ncpu', $output, $ret);
|
||||
if ($ret !== 0) {
|
||||
throw new RuntimeException('Failed to get cpu count');
|
||||
}
|
||||
|
||||
return (int) $output[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取不同架构对应的 cflags 参数
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getArchCFlags(string $arch): string
|
||||
{
|
||||
return match ($arch) {
|
||||
'x86_64' => '--target=x86_64-apple-darwin',
|
||||
'arm64','aarch64' => '--target=arm64-apple-darwin',
|
||||
default => throw new RuntimeException('unsupported arch: ' . $arch),
|
||||
};
|
||||
}
|
||||
}
|
||||
43
src/SPC/builder/macos/library/MacOSLibraryBase.php
Normal file
43
src/SPC/builder/macos/library/MacOSLibraryBase.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\LibraryBase;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\builder\traits\UnixLibraryTrait;
|
||||
use SPC\store\Config;
|
||||
|
||||
abstract class MacOSLibraryBase extends LibraryBase
|
||||
{
|
||||
use UnixLibraryTrait;
|
||||
|
||||
protected array $static_libs;
|
||||
|
||||
protected array $headers;
|
||||
|
||||
/**
|
||||
* 依赖的名字及是否可选,例如:curl => true,代表依赖 curl 但可选
|
||||
*/
|
||||
protected array $dep_names;
|
||||
|
||||
public function __construct(protected MacOSBuilder $builder)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function getBuilder(): BuilderBase
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前 lib 库依赖的 macOS framework
|
||||
*/
|
||||
public function getFrameworks(): array
|
||||
{
|
||||
return Config::getLib(static::NAME, 'frameworks', []);
|
||||
}
|
||||
}
|
||||
49
src/SPC/builder/macos/library/brotli.php
Normal file
49
src/SPC/builder/macos/library/brotli.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class brotli extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'brotli';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
f_passthru(
|
||||
"{$this->builder->set_x} && " .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"cmake --build . -j {$this->builder->concurrency} && " .
|
||||
'make install DESTDIR="' . $destdir . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
39
src/SPC/builder/macos/library/bzip2.php
Normal file
39
src/SPC/builder/macos/library/bzip2.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class bzip2 extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'bzip2';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' .
|
||||
"make -j{$this->builder->concurrency} {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' libbz2.a" . ' && ' .
|
||||
// make install may fail when cross-compiling, so we copy files.
|
||||
'cp libbz2.a ' . BUILD_LIB_PATH . ' && ' .
|
||||
'cp bzlib.h ' . BUILD_INCLUDE_PATH
|
||||
);
|
||||
}
|
||||
}
|
||||
118
src/SPC/builder/macos/library/curl.php
Normal file
118
src/SPC/builder/macos/library/curl.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class curl extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'curl';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$extra = '';
|
||||
// lib:openssl
|
||||
$openssl = $this->getBuilder()->getLib('openssl');
|
||||
if ($openssl instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DCURL_USE_OPENSSL=ON ';
|
||||
} else {
|
||||
$extra .= '-DCURL_USE_OPENSSL=OFF -DCURL_ENABLE_SSL=OFF ';
|
||||
}
|
||||
// lib:zlib
|
||||
$zlib = $this->getBuilder()->getLib('zlib');
|
||||
if ($zlib instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
|
||||
}
|
||||
// lib:libssh2
|
||||
$libssh2 = $this->builder->getLib('libssh2');
|
||||
if ($libssh2 instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DLIBSSH2_LIBRARY="' . $libssh2->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DLIBSSH2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DCURL_USE_LIBSSH2=OFF ';
|
||||
}
|
||||
// lib:brotli
|
||||
$brotli = $this->builder->getLib('brotli');
|
||||
if ($brotli) {
|
||||
$extra .= '-DCURL_BROTLI=ON ' .
|
||||
'-DBROTLIDEC_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlidec-static.a') . ';' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' .
|
||||
'-DBROTLICOMMON_LIBRARY="' . realpath(BUILD_LIB_PATH . '/libbrotlicommon-static.a') . '" ' .
|
||||
'-DBROTLI_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DCURL_BROTLI=OFF ';
|
||||
}
|
||||
// lib:nghttp2
|
||||
$nghttp2 = $this->builder->getLib('nghttp2');
|
||||
if ($nghttp2 instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DUSE_NGHTTP2=ON ' .
|
||||
'-DNGHTTP2_LIBRARY="' . $nghttp2->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DNGHTTP2_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DUSE_NGHTTP2=OFF ';
|
||||
}
|
||||
// lib:ldap
|
||||
$ldap = $this->builder->getLib('ldap');
|
||||
if ($ldap instanceof MacOSLibraryBase) {
|
||||
// $extra .= '-DCURL_DISABLE_LDAP=OFF ';
|
||||
// TODO: LDAP support
|
||||
throw new RuntimeException('LDAP support is not implemented yet');
|
||||
}
|
||||
$extra .= '-DCURL_DISABLE_LDAP=ON ';
|
||||
// lib:zstd
|
||||
$zstd = $this->builder->getLib('zstd');
|
||||
if ($zstd instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DCURL_ZSTD=ON ' .
|
||||
'-DZstd_LIBRARY="' . $zstd->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DCURL_ZSTD=OFF ';
|
||||
}
|
||||
// lib:idn2
|
||||
$idn2 = $this->builder->getLib('idn2');
|
||||
$extra .= $idn2 instanceof MacOSLibraryBase ? '-DUSE_LIBIDN2=ON ' : '-DUSE_LIBIDN2=OFF ';
|
||||
// lib:psl
|
||||
$libpsl = $this->builder->getLib('psl');
|
||||
$extra .= $libpsl instanceof MacOSLibraryBase ? '-DCURL_USE_LIBPSL=ON ' : '-DCURL_USE_LIBPSL=OFF ';
|
||||
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
// compile!
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
$extra .
|
||||
'-DCMAKE_INSTALL_PREFIX= ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR="' . $destdir . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
45
src/SPC/builder/macos/library/libffi.php
Normal file
45
src/SPC/builder/macos/library/libffi.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class libffi extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libffi';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
'--enable-static ' .
|
||||
'--disable-shared ' .
|
||||
"--host={$this->builder->arch}-apple-darwin " .
|
||||
"--target={$this->builder->arch}-apple-darwin " .
|
||||
'--prefix= ' . // use prefix=/
|
||||
"--libdir={$lib} && " .
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
"make install DESTDIR={$destdir}"
|
||||
);
|
||||
}
|
||||
}
|
||||
65
src/SPC/builder/macos/library/libpng.php
Normal file
65
src/SPC/builder/macos/library/libpng.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\util\Patcher;
|
||||
|
||||
class libpng extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libpng';
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
// 不同架构的专属优化
|
||||
$optimizations = match ($this->builder->arch) {
|
||||
'x86_64' => '--enable-intel-sse ',
|
||||
'arm64' => '--enable-arm-neon ',
|
||||
default => '',
|
||||
};
|
||||
|
||||
// patch configure
|
||||
Patcher::patchUnixLibpng();
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} " .
|
||||
'./configure ' .
|
||||
"--host={$this->builder->gnu_arch}-apple-darwin " .
|
||||
'--disable-shared ' .
|
||||
'--enable-static ' .
|
||||
'--enable-hardware-optimizations ' .
|
||||
$optimizations .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I. -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la && " .
|
||||
'make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH . ' && ' .
|
||||
'cd ' . BUILD_LIB_PATH . ' && ' .
|
||||
'ln -sf libpng16.a libpng.a'
|
||||
);
|
||||
}
|
||||
}
|
||||
56
src/SPC/builder/macos/library/libssh2.php
Normal file
56
src/SPC/builder/macos/library/libssh2.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class libssh2 extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libssh2';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
// lib:zlib
|
||||
$enable_zlib = $this->builder->getLib('zlib') !== null ? 'ON' : 'OFF';
|
||||
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_EXAMPLES=OFF ' .
|
||||
'-DBUILD_TESTING=OFF ' .
|
||||
"-DENABLE_ZLIB_COMPRESSION={$enable_zlib} " .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"cmake --build . -j {$this->builder->concurrency} --target libssh2 && " .
|
||||
'make install DESTDIR="' . $destdir . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
66
src/SPC/builder/macos/library/libxml2.php
Normal file
66
src/SPC/builder/macos/library/libxml2.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class libxml2 extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libxml2';
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
protected function build()
|
||||
{
|
||||
$enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF';
|
||||
$enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF';
|
||||
$enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF';
|
||||
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DLIBXML2_WITH_ICONV=ON ' .
|
||||
"-DLIBXML2_WITH_ZLIB={$enable_zlib} " .
|
||||
"-DLIBXML2_WITH_ICU={$enable_icu} " .
|
||||
"-DLIBXML2_WITH_LZMA={$enable_xz} " .
|
||||
'-DLIBXML2_WITH_PYTHON=OFF ' .
|
||||
'-DLIBXML2_WITH_PROGRAMS=OFF ' .
|
||||
'-DLIBXML2_WITH_TESTS=OFF ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"cmake --build . -j {$this->builder->concurrency} && " .
|
||||
'make install DESTDIR="' . $destdir . '"'
|
||||
);
|
||||
}
|
||||
}
|
||||
95
src/SPC/builder/macos/library/libyaml.php
Normal file
95
src/SPC/builder/macos/library/libyaml.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
class libyaml extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libyaml';
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
// prepare cmake/config.h.in
|
||||
if (!is_file(SOURCE_PATH . '/libyaml/cmake/config.h.in')) {
|
||||
f_mkdir(SOURCE_PATH . '/libyaml/cmake');
|
||||
file_put_contents(
|
||||
SOURCE_PATH . '/libyaml/cmake/config.h.in',
|
||||
<<<'EOF'
|
||||
#define YAML_VERSION_MAJOR @YAML_VERSION_MAJOR@
|
||||
#define YAML_VERSION_MINOR @YAML_VERSION_MINOR@
|
||||
#define YAML_VERSION_PATCH @YAML_VERSION_PATCH@
|
||||
#define YAML_VERSION_STRING "@YAML_VERSION_STRING@"
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
// prepare yamlConfig.cmake.in
|
||||
if (!is_file(SOURCE_PATH . '/libyaml/yamlConfig.cmake.in')) {
|
||||
file_put_contents(
|
||||
SOURCE_PATH . '/libyaml/yamlConfig.cmake.in',
|
||||
<<<'EOF'
|
||||
# Config file for the yaml library.
|
||||
#
|
||||
# It defines the following variables:
|
||||
# yaml_LIBRARIES - libraries to link against
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(yaml_TARGETS "@PACKAGE_CONFIG_DIR_CONFIG@/yamlTargets.cmake")
|
||||
|
||||
if(NOT yaml_TARGETS_IMPORTED)
|
||||
set(yaml_TARGETS_IMPORTED 1)
|
||||
include(${yaml_TARGETS})
|
||||
endif()
|
||||
|
||||
set(yaml_LIBRARIES yaml)
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DBUILD_TESTING=OFF ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
101
src/SPC/builder/macos/library/libzip.php
Normal file
101
src/SPC/builder/macos/library/libzip.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class libzip extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'libzip';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$extra = '';
|
||||
// lib:zlib
|
||||
$zlib = $this->builder->getLib('zlib');
|
||||
if ($zlib instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DZLIB_LIBRARY="' . $zlib->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DZLIB_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
|
||||
}
|
||||
// lib:bzip2
|
||||
$libbzip2 = $this->builder->getLib('bzip2');
|
||||
if ($libbzip2 instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DENABLE_BZIP2=ON ' .
|
||||
'-DBZIP2_LIBRARIES="' . $libbzip2->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DBZIP2_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
|
||||
} else {
|
||||
$extra .= '-DENABLE_BZIP2=OFF ';
|
||||
}
|
||||
// lib:xz
|
||||
$xz = $this->builder->getLib('xz');
|
||||
if ($xz instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DENABLE_LZMA=ON ' .
|
||||
'-DLIBLZMA_LIBRARY="' . $xz->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DLIBLZMA_INCLUDE_DIR=' . BUILD_INCLUDE_PATH . ' ';
|
||||
} else {
|
||||
$extra .= '-DENABLE_LZMA=OFF ';
|
||||
}
|
||||
// lib:zstd
|
||||
$libzstd = $this->builder->getLib('zstd');
|
||||
if ($libzstd instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DENABLE_ZSTD=ON ' .
|
||||
'-DZstd_LIBRARY="' . $libzstd->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DZstd_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DENABLE_ZSTD=OFF ';
|
||||
}
|
||||
// lib:openssl
|
||||
$libopenssl = $this->builder->getLib('openssl');
|
||||
if ($libopenssl instanceof MacOSLibraryBase) {
|
||||
$extra .= '-DENABLE_OPENSSL=ON ' .
|
||||
'-DOpenSSL_LIBRARY="' . $libopenssl->getStaticLibFiles(style: 'cmake') . '" ' .
|
||||
'-DOpenSSL_INCLUDE_DIR="' . BUILD_INCLUDE_PATH . '" ';
|
||||
} else {
|
||||
$extra .= '-DENABLE_OPENSSL=OFF ';
|
||||
}
|
||||
|
||||
[$lib, $include, $destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'rm -rf build && ' .
|
||||
'mkdir -p build && ' .
|
||||
'cd build && ' .
|
||||
"{$this->builder->configure_env} " . ' cmake ' .
|
||||
// '--debug-find ' .
|
||||
'-DCMAKE_BUILD_TYPE=Release ' .
|
||||
'-DENABLE_GNUTLS=OFF ' .
|
||||
'-DENABLE_MBEDTLS=OFF ' .
|
||||
'-DBUILD_SHARED_LIBS=OFF ' .
|
||||
'-DBUILD_DOC=OFF ' .
|
||||
'-DBUILD_EXAMPLES=OFF ' .
|
||||
'-DBUILD_REGRESS=OFF ' .
|
||||
'-DBUILD_TOOLS=OFF ' .
|
||||
$extra .
|
||||
'-DCMAKE_INSTALL_PREFIX=/ ' .
|
||||
"-DCMAKE_INSTALL_LIBDIR={$lib} " .
|
||||
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
|
||||
"-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " .
|
||||
'.. && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
63
src/SPC/builder/macos/library/nghttp2.php
Normal file
63
src/SPC/builder/macos/library/nghttp2.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class nghttp2 extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'nghttp2';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
$args = $this->builder->makeAutoconfArgs(static::NAME, [
|
||||
'zlib' => null,
|
||||
'openssl' => null,
|
||||
'libxml2' => null,
|
||||
'libev' => null,
|
||||
'libcares' => null,
|
||||
'libngtcp2' => null,
|
||||
'libnghttp3' => null,
|
||||
'libbpf' => null,
|
||||
'libevent-openssl' => null,
|
||||
'jansson' => null,
|
||||
'jemalloc' => null,
|
||||
'systemd' => null,
|
||||
'cunit' => null,
|
||||
]);
|
||||
|
||||
[,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} " . ' ./configure ' .
|
||||
'--enable-static ' .
|
||||
'--disable-shared ' .
|
||||
"--host={$this->builder->gnu_arch}-apple-darwin " .
|
||||
'--enable-lib-only ' .
|
||||
'--with-boost=no ' .
|
||||
$args . ' ' .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
"make install DESTDIR={$destdir}"
|
||||
);
|
||||
}
|
||||
}
|
||||
44
src/SPC/builder/macos/library/onig.php
Normal file
44
src/SPC/builder/macos/library/onig.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class onig extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'onig';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} " . ' ./configure ' .
|
||||
'--enable-static ' .
|
||||
'--disable-shared ' .
|
||||
"--host={$this->builder->arch}-apple-darwin " .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
52
src/SPC/builder/macos/library/openssl.php
Normal file
52
src/SPC/builder/macos/library/openssl.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class openssl extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'openssl';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[$lib,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
// lib:zlib
|
||||
$extra = '';
|
||||
$ex_lib = '';
|
||||
$zlib = $this->builder->getLib('zlib');
|
||||
if ($zlib instanceof MacOSLibraryBase) {
|
||||
$extra = 'zlib';
|
||||
$ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib);
|
||||
}
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} ./Configure no-shared {$extra} " .
|
||||
'--prefix=/ ' . // use prefix=/
|
||||
"--libdir={$lib} " .
|
||||
" darwin64-{$this->builder->arch}-cc && " .
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\" && " .
|
||||
'make install_sw DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
42
src/SPC/builder/macos/library/sqlite.php
Normal file
42
src/SPC/builder/macos/library/sqlite.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class sqlite extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'sqlite';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
'--enable-static --disable-shared ' .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
52
src/SPC/builder/macos/library/xz.php
Normal file
52
src/SPC/builder/macos/library/xz.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class xz extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'xz';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
'autoreconf -i --force && ' .
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
'--enable-static ' .
|
||||
'--disable-shared ' .
|
||||
"--host={$this->builder->gnu_arch}-apple-darwin " .
|
||||
'--disable-xz ' .
|
||||
'--disable-xzdec ' .
|
||||
'--disable-lzmadec ' .
|
||||
'--disable-lzmainfo ' .
|
||||
'--disable-scripts ' .
|
||||
'--disable-doc ' .
|
||||
'--with-libiconv ' .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
42
src/SPC/builder/macos/library/zlib.php
Normal file
42
src/SPC/builder/macos/library/zlib.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class zlib extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'zlib';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
[,,$destdir] = SEPARATED_PATH;
|
||||
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"{$this->builder->configure_env} ./configure " .
|
||||
'--static ' .
|
||||
'--prefix= && ' . // use prefix=/
|
||||
'make clean && ' .
|
||||
"make -j{$this->builder->concurrency} && " .
|
||||
'make install DESTDIR=' . $destdir
|
||||
);
|
||||
}
|
||||
}
|
||||
41
src/SPC/builder/macos/library/zstd.php
Normal file
41
src/SPC/builder/macos/library/zstd.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2022 Yun Dou <dixyes@gmail.com>
|
||||
*
|
||||
* lwmbs is licensed under Mulan PSL v2. You can use this
|
||||
* software according to the terms and conditions of the
|
||||
* Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at:
|
||||
*
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\macos\library;
|
||||
|
||||
class zstd extends MacOSLibraryBase
|
||||
{
|
||||
public const NAME = 'zstd';
|
||||
|
||||
protected function build()
|
||||
{
|
||||
f_passthru(
|
||||
$this->builder->set_x . ' && ' .
|
||||
"cd {$this->source_dir} && " .
|
||||
"make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean" . ' && ' .
|
||||
"make -j{$this->builder->concurrency} " .
|
||||
"{$this->builder->configure_env} " .
|
||||
"PREFIX='" . BUILD_ROOT_PATH . "' " .
|
||||
'-C lib libzstd.a CPPFLAGS_STATLIB=-DZSTD_MULTITHREAD && ' .
|
||||
'cp lib/libzstd.a ' . BUILD_LIB_PATH . ' && ' .
|
||||
'cp lib/zdict.h lib/zstd_errors.h lib/zstd.h ' . BUILD_INCLUDE_PATH
|
||||
);
|
||||
}
|
||||
}
|
||||
9
src/SPC/builder/traits/LibraryTrait.php
Normal file
9
src/SPC/builder/traits/LibraryTrait.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\traits;
|
||||
|
||||
trait LibraryTrait
|
||||
{
|
||||
}
|
||||
13
src/SPC/builder/traits/NoMotdTrait.php
Normal file
13
src/SPC/builder/traits/NoMotdTrait.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\traits;
|
||||
|
||||
/**
|
||||
* 仅供 Command 使用,如果使用了该 Trait,则在执行对应命令时不打印 motd
|
||||
*/
|
||||
trait NoMotdTrait
|
||||
{
|
||||
protected bool $no_motd = true;
|
||||
}
|
||||
102
src/SPC/builder/traits/UnixBuilderTrait.php
Normal file
102
src/SPC/builder/traits/UnixBuilderTrait.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\traits;
|
||||
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
trait UnixBuilderTrait
|
||||
{
|
||||
/** @var string 设置的命令前缀,设置为 set -x 可以在终端打印命令 */
|
||||
public string $set_x = 'set -x';
|
||||
|
||||
/** @var string C 编译器命令 */
|
||||
public string $cc;
|
||||
|
||||
/** @var string C++ 编译器命令 */
|
||||
public string $cxx;
|
||||
|
||||
/** @var string cflags 参数 */
|
||||
public string $arch_c_flags;
|
||||
|
||||
/** @var string C++ flags 参数 */
|
||||
public string $arch_cxx_flags;
|
||||
|
||||
/** @var string cmake toolchain file */
|
||||
public string $cmake_toolchain_file;
|
||||
|
||||
/** @var string configure 环境依赖的变量 */
|
||||
public string $configure_env;
|
||||
|
||||
public function getAllStaticLibFiles(): array
|
||||
{
|
||||
$libs = [];
|
||||
|
||||
// reorder libs
|
||||
foreach ($this->libs as $lib) {
|
||||
foreach ($lib->getDependencies() as $dep) {
|
||||
$libs[] = $dep;
|
||||
}
|
||||
$libs[] = $lib;
|
||||
}
|
||||
|
||||
$libFiles = [];
|
||||
$libNames = [];
|
||||
// merge libs
|
||||
foreach ($libs as $lib) {
|
||||
if (!in_array($lib::NAME, $libNames, true)) {
|
||||
$libNames[] = $lib::NAME;
|
||||
array_unshift($libFiles, ...$lib->getStaticLibs());
|
||||
}
|
||||
}
|
||||
return array_map(fn ($x) => realpath(BUILD_LIB_PATH . "/{$x}"), $libFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function sanityCheck(int $build_micro_rule): void
|
||||
{
|
||||
logger()->info('running sanity check');
|
||||
if ($build_micro_rule !== BUILD_MICRO_ONLY) {
|
||||
f_exec(
|
||||
$this->set_x . ' && ' .
|
||||
SOURCE_PATH . '/php-src/sapi/cli/php -r "echo \"hello\";"',
|
||||
$output,
|
||||
$ret
|
||||
);
|
||||
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
|
||||
throw new RuntimeException('cli failed sanity check');
|
||||
}
|
||||
foreach ($this->exts as $ext) {
|
||||
logger()->debug('checking ext: ' . $ext->getName());
|
||||
if (file_exists(ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php')) {
|
||||
f_exec(
|
||||
$this->set_x . ' && ' . SOURCE_PATH . '/php-src/sapi/cli/php ' . ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php',
|
||||
$output,
|
||||
$ret
|
||||
);
|
||||
if ($ret !== 0) {
|
||||
throw new RuntimeException('extension ' . $ext->getName() . ' failed sanity check');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($build_micro_rule !== BUILD_MICRO_NONE) {
|
||||
if (file_exists(SOURCE_PATH . '/hello.exe')) {
|
||||
@unlink(SOURCE_PATH . '/hello.exe');
|
||||
}
|
||||
file_put_contents(
|
||||
SOURCE_PATH . '/hello.exe',
|
||||
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
|
||||
'<?php echo "hello";'
|
||||
);
|
||||
chmod(SOURCE_PATH . '/hello.exe', 0755);
|
||||
f_exec(SOURCE_PATH . '/hello.exe', $output2, $ret);
|
||||
if ($ret !== 0 || trim($out = implode('', $output2)) !== 'hello') {
|
||||
throw new RuntimeException('micro failed sanity check, ret[' . $ret . '], out[' . ($out ?? 'NULL') . ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/SPC/builder/traits/UnixLibraryTrait.php
Normal file
53
src/SPC/builder/traits/UnixLibraryTrait.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\traits;
|
||||
|
||||
use SPC\builder\LibraryBase;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
trait UnixLibraryTrait
|
||||
{
|
||||
use LibraryTrait;
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string
|
||||
{
|
||||
$libs = [$this];
|
||||
if ($recursive) {
|
||||
array_unshift($libs, ...array_values($this->getDependencies(recursive: true)));
|
||||
}
|
||||
|
||||
$sep = match ($style) {
|
||||
'autoconf' => ' ',
|
||||
'cmake' => ';',
|
||||
default => throw new RuntimeException('style only support autoconf and cmake'),
|
||||
};
|
||||
$ret = [];
|
||||
/** @var LibraryBase $lib */
|
||||
foreach ($libs as $lib) {
|
||||
$libFiles = [];
|
||||
foreach ($lib->getStaticLibs() as $name) {
|
||||
$name = str_replace(' ', '\ ', realpath(BUILD_LIB_PATH . "/{$name}"));
|
||||
$name = str_replace('"', '\"', $name);
|
||||
$libFiles[] = $name;
|
||||
}
|
||||
array_unshift($ret, implode($sep, $libFiles));
|
||||
}
|
||||
return implode($sep, $ret);
|
||||
}
|
||||
|
||||
public function makeAutoconfEnv(string $prefix = null): string
|
||||
{
|
||||
if ($prefix === null) {
|
||||
$prefix = str_replace('-', '_', strtoupper(static::NAME));
|
||||
}
|
||||
return $prefix . '_CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' .
|
||||
$prefix . '_LIBS="' . $this->getStaticLibFiles() . '"';
|
||||
}
|
||||
}
|
||||
68
src/SPC/builder/traits/UnixSystemUtilTrait.php
Normal file
68
src/SPC/builder/traits/UnixSystemUtilTrait.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\builder\traits;
|
||||
|
||||
/**
|
||||
* Unix 系统的工具函数 Trait,适用于 Linux、macOS
|
||||
*/
|
||||
trait UnixSystemUtilTrait
|
||||
{
|
||||
/**
|
||||
* 生成 toolchain.cmake,用于 cmake 构建
|
||||
*
|
||||
* @param string $os 操作系统代号
|
||||
* @param string $target_arch 目标架构
|
||||
* @param string $cflags CFLAGS 参数
|
||||
* @param null|string $cc CC 参数(默认空)
|
||||
* @param null|string $cxx CXX 参数(默认空)
|
||||
*/
|
||||
public static function makeCmakeToolchainFile(
|
||||
string $os,
|
||||
string $target_arch,
|
||||
string $cflags,
|
||||
?string $cc = null,
|
||||
?string $cxx = null
|
||||
): string {
|
||||
logger()->debug("making cmake tool chain file for {$os} {$target_arch} with CFLAGS='{$cflags}'");
|
||||
$root = BUILD_ROOT_PATH;
|
||||
$ccLine = '';
|
||||
if ($cc) {
|
||||
$ccLine = 'SET(CMAKE_C_COMPILER ' . self::findCommand($cc) . ')';
|
||||
}
|
||||
$cxxLine = '';
|
||||
if ($cxx) {
|
||||
$cxxLine = 'SET(CMAKE_CXX_COMPILER ' . self::findCommand($cxx) . ')';
|
||||
}
|
||||
$toolchain = <<<CMAKE
|
||||
SET(CMAKE_SYSTEM_NAME {$os})
|
||||
SET(CMAKE_SYSTEM_PROCESSOR {$target_arch})
|
||||
{$ccLine}
|
||||
{$cxxLine}
|
||||
SET(CMAKE_C_FLAGS "{$cflags}")
|
||||
SET(CMAKE_CXX_FLAGS "{$cflags}")
|
||||
SET(CMAKE_FIND_ROOT_PATH "{$root}")
|
||||
CMAKE;
|
||||
file_put_contents(SOURCE_PATH . '/toolchain.cmake', $toolchain);
|
||||
return realpath(SOURCE_PATH . '/toolchain.cmake');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name 命令名称
|
||||
* @param array $paths 寻找的目标路径(如果不传入,则使用环境变量 PATH)
|
||||
* @return null|string 找到了返回命令路径,找不到返回 null
|
||||
*/
|
||||
public static function findCommand(string $name, array $paths = []): ?string
|
||||
{
|
||||
if (!$paths) {
|
||||
$paths = explode(PATH_SEPARATOR, getenv('PATH'));
|
||||
}
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path . DIRECTORY_SEPARATOR . $name)) {
|
||||
return $path . DIRECTORY_SEPARATOR . $name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
59
src/SPC/command/BaseCommand.php
Normal file
59
src/SPC/command/BaseCommand.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use SPC\ConsoleApplication;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use ZM\Logger\ConsoleLogger;
|
||||
|
||||
abstract class BaseCommand extends Command
|
||||
{
|
||||
public function __construct(string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->addOption('debug', null, null, 'Enable debug mode');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// 注册全局错误处理器
|
||||
set_error_handler(static function ($error_no, $error_msg, $error_file, $error_line) {
|
||||
$tips = [
|
||||
E_WARNING => ['PHP Warning: ', 'warning'],
|
||||
E_NOTICE => ['PHP Notice: ', 'notice'],
|
||||
E_USER_ERROR => ['PHP Error: ', 'error'],
|
||||
E_USER_WARNING => ['PHP Warning: ', 'warning'],
|
||||
E_USER_NOTICE => ['PHP Notice: ', 'notice'],
|
||||
E_STRICT => ['PHP Strict: ', 'notice'],
|
||||
E_RECOVERABLE_ERROR => ['PHP Recoverable Error: ', 'error'],
|
||||
E_DEPRECATED => ['PHP Deprecated: ', 'notice'],
|
||||
E_USER_DEPRECATED => ['PHP User Deprecated: ', 'notice'],
|
||||
];
|
||||
$level_tip = $tips[$error_no] ?? ['PHP Unknown: ', 'error'];
|
||||
$error = $level_tip[0] . $error_msg . ' in ' . $error_file . ' on ' . $error_line;
|
||||
logger()->{$level_tip[1]}($error);
|
||||
// 如果 return false 则错误会继续递交给 PHP 标准错误处理
|
||||
return true;
|
||||
}, E_ALL | E_STRICT);
|
||||
if ($input->getOption('debug')) {
|
||||
global $ob_logger;
|
||||
$ob_logger = new ConsoleLogger(LogLevel::DEBUG);
|
||||
define('DEBUG_MODE', true);
|
||||
}
|
||||
$version = ConsoleApplication::VERSION;
|
||||
if (!isset($this->no_motd)) {
|
||||
echo " _ _ _ _
|
||||
___| |_ __ _| |_(_) ___ _ __ | |__ _ __
|
||||
/ __| __/ _` | __| |/ __|____| '_ \\| '_ \\| '_ \\
|
||||
\\__ \\ || (_| | |_| | (_|_____| |_) | | | | |_) |
|
||||
|___/\\__\\__,_|\\__|_|\\___| | .__/|_| |_| .__/ v{$version}
|
||||
|_| |_|
|
||||
";
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/SPC/command/BuildCliCommand.php
Normal file
86
src/SPC/command/BuildCliCommand.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\util\DependencyUtil;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
class BuildCliCommand extends BuildCommand
|
||||
{
|
||||
protected static $defaultName = 'build';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('Build CLI binary');
|
||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||
$this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', '');
|
||||
$this->addOption('build-micro', null, null, 'build micro only');
|
||||
$this->addOption('build-all', null, null, 'build both cli and micro');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// 从参数中获取要编译的 libraries,并转换为数组
|
||||
$libraries = array_map('trim', array_filter(explode(',', $input->getOption('with-libs'))));
|
||||
// 从参数中获取要编译的 extensions,并转换为数组
|
||||
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
|
||||
|
||||
define('BUILD_ALL_STATIC', true);
|
||||
|
||||
if ($input->getOption('build-all')) {
|
||||
$rule = BUILD_MICRO_BOTH;
|
||||
logger()->info('Builder will build php-cli and phpmicro SAPI');
|
||||
} elseif ($input->getOption('build-micro')) {
|
||||
$rule = BUILD_MICRO_ONLY;
|
||||
logger()->info('Builder will build phpmicro SAPI');
|
||||
} else {
|
||||
$rule = BUILD_MICRO_NONE;
|
||||
logger()->info('Builder will build php-cli SAPI');
|
||||
}
|
||||
try {
|
||||
// 构建对象
|
||||
$builder = BuilderProvider::makeBuilderByInput($input);
|
||||
// 根据提供的扩展列表获取依赖库列表并编译
|
||||
[$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries);
|
||||
|
||||
logger()->info('Enabled extensions: ' . implode(', ', $extensions));
|
||||
logger()->info('Required libraries: ' . implode(', ', $libraries));
|
||||
if (!empty($not_included)) {
|
||||
logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included));
|
||||
}
|
||||
sleep(2);
|
||||
// 编译和检查库是否完整
|
||||
$builder->buildLibs($libraries);
|
||||
// 执行扩展检测
|
||||
$builder->proveExts($extensions);
|
||||
// 构建
|
||||
$builder->buildPHP($rule, $input->getOption('with-clean'), $input->getOption('bloat'));
|
||||
// 统计时间
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build complete, used ' . $time . ' s !');
|
||||
if ($rule !== BUILD_MICRO_ONLY) {
|
||||
logger()->info('Static php binary path: ' . SOURCE_PATH . '/php-src/sapi/cli/php');
|
||||
}
|
||||
if ($rule !== BUILD_MICRO_NONE) {
|
||||
logger()->info('phpmicro binary path: ' . SOURCE_PATH . '/php-src/sapi/micro/micro.sfx');
|
||||
}
|
||||
return 0;
|
||||
} catch (\Throwable $e) {
|
||||
if ($input->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->emergency('Build failed, please check terminal output, or build with --debug option to see more details.');
|
||||
logger()->emergency($e->getMessage());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/SPC/command/BuildCommand.php
Normal file
37
src/SPC/command/BuildCommand.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
abstract class BuildCommand extends BaseCommand
|
||||
{
|
||||
public function __construct(string $name = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
// 根据运行的操作系统分配允许不同的命令行参数,Windows 需要额外的 VS 和 SDK等,*nix 需要提供架构
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
$this->addOption('with-sdk-binary-dir', null, InputOption::VALUE_REQUIRED, 'path to binary sdk');
|
||||
$this->addOption('vs-ver', null, InputOption::VALUE_REQUIRED, 'vs version, e.g. "17" for Visual Studio 2022');
|
||||
$this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture, "x64" or "arm64"', 'x64');
|
||||
break;
|
||||
case 'Linux':
|
||||
$this->addOption('no-system-static', null, null, 'do not use system static libraries');
|
||||
// no break
|
||||
case 'Darwin':
|
||||
$this->addOption('cc', null, InputOption::VALUE_REQUIRED, 'C compiler');
|
||||
$this->addOption('cxx', null, InputOption::VALUE_REQUIRED, 'C++ compiler');
|
||||
$this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture', php_uname('m'));
|
||||
break;
|
||||
}
|
||||
|
||||
// 是否在编译 make 前清除旧的文件
|
||||
$this->addOption('with-clean', null, null, 'fresh build, `make clean` before `make`');
|
||||
// 是否采用强制链接,让链接器强制加载静态库文件
|
||||
$this->addOption('bloat', null, null, 'add all libraries into binary');
|
||||
}
|
||||
}
|
||||
69
src/SPC/command/BuildLibsCommand.php
Normal file
69
src/SPC/command/BuildLibsCommand.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\BuilderProvider;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
class BuildLibsCommand extends BuildCommand
|
||||
{
|
||||
protected static $defaultName = 'build:libs';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('Build dependencies');
|
||||
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||
$this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// --all 等于 ""
|
||||
if ($input->getOption('all')) {
|
||||
$input->setArgument('libraries', '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// 从参数中获取要编译的 libraries,并转换为数组
|
||||
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
|
||||
|
||||
// 删除旧资源
|
||||
if ($input->getOption('clean')) {
|
||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||
logger()->warning(BUILD_ROOT_PATH);
|
||||
logger()->warning('I will remove these dir after you press [Enter] !');
|
||||
echo 'Confirm operation? [Yes] ';
|
||||
fgets(STDIN);
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH);
|
||||
} else {
|
||||
f_passthru('rm -rf ' . BUILD_ROOT_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
// 构建对象
|
||||
$builder = BuilderProvider::makeBuilderByInput($input);
|
||||
// 只编译 library 的情况下,标记
|
||||
$builder->setLibsOnly();
|
||||
// 编译和检查库完整
|
||||
$builder->buildLibs($libraries);
|
||||
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Build libs complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
116
src/SPC/command/DeployCommand.php
Normal file
116
src/SPC/command/DeployCommand.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use CliHelper\Tools\ArgFixer;
|
||||
use CliHelper\Tools\DataProvider;
|
||||
use CliHelper\Tools\SeekableArrayIterator;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
class DeployCommand extends BaseCommand
|
||||
{
|
||||
protected static $defaultName = 'deploy-self';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('Deploy static-php-cli self to an .phar application');
|
||||
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
|
||||
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
|
||||
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
|
||||
$this->addOption('disable-gzip', 'z', InputOption::VALUE_NONE, 'disable gzip archive mode');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// 第一阶段流程:如果没有写path,将会提示输入要打包的path
|
||||
$prompt = new ArgFixer($input, $output);
|
||||
// 首先得确认是不是关闭了readonly模式
|
||||
if (ini_get('phar.readonly') == 1) {
|
||||
if ($input->getOption('auto-phar-fix')) {
|
||||
$ask = true;
|
||||
} else {
|
||||
$ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
|
||||
}
|
||||
$output->writeln('<info>Now running command in child process.</info>');
|
||||
if ($ask) {
|
||||
global $argv;
|
||||
passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode);
|
||||
exit($retcode);
|
||||
}
|
||||
}
|
||||
// 获取路径
|
||||
$path = WORKING_DIR;
|
||||
// 如果是目录,则将目录下的所有文件打包
|
||||
$phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
|
||||
|
||||
if (DataProvider::isRelativePath($phar_path)) {
|
||||
$phar_path = '/tmp/' . $phar_path;
|
||||
}
|
||||
if (file_exists($phar_path)) {
|
||||
$ask = $input->getOption('overwrite') ? true : $prompt->requireBool('<comment>The file "' . $phar_path . '" already exists, do you want to overwrite it?</comment>' . PHP_EOL . 'If you want to, just Enter');
|
||||
if (!$ask) {
|
||||
$output->writeln('<comment>User canceled.</comment>');
|
||||
return 1;
|
||||
}
|
||||
@unlink($phar_path);
|
||||
}
|
||||
$phar = new \Phar($phar_path);
|
||||
$phar->startBuffering();
|
||||
|
||||
$all = DataProvider::scanDirFiles($path, true, true);
|
||||
|
||||
$all = array_filter($all, function ($x) {
|
||||
$dirs = preg_match('/(^(bin|config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x);
|
||||
return !($dirs !== 1);
|
||||
});
|
||||
sort($all);
|
||||
$map = [];
|
||||
foreach ($all as $v) {
|
||||
$map[$v] = $path . '/' . $v;
|
||||
}
|
||||
|
||||
$output->writeln('<info>Start packing files...</info>');
|
||||
try {
|
||||
$phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
|
||||
$phar->addFromString(
|
||||
'.phar-entry.php',
|
||||
str_replace(
|
||||
'/../vendor/autoload.php',
|
||||
'/vendor/autoload.php',
|
||||
file_get_contents(ROOT_DIR . '/bin/spc')
|
||||
)
|
||||
);
|
||||
$stub = '.phar-entry.php';
|
||||
$phar->setStub($phar->createDefaultStub($stub));
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeln($e);
|
||||
return 1;
|
||||
}
|
||||
$phar->addFromString('.prod', 'true');
|
||||
if (!$input->getOption('disable-gzip')) {
|
||||
$phar->compressFiles(\Phar::GZ);
|
||||
}
|
||||
$phar->stopBuffering();
|
||||
$output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".');
|
||||
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) {
|
||||
$output->writeln('Detected you have already compiled micro binary, I will make executable now for you!');
|
||||
file_put_contents(
|
||||
$phar_path . '.exe',
|
||||
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
|
||||
file_get_contents($phar_path)
|
||||
);
|
||||
chmod($phar_path . '.exe', 0755);
|
||||
$output->writeln('<info>Static: ' . $phar_path . '.exe</info>');
|
||||
}
|
||||
chmod($phar_path, 0755);
|
||||
$output->writeln('<info>Phar: ' . $phar_path . '</info>');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
29
src/SPC/command/DumpLicenseCommand.php
Normal file
29
src/SPC/command/DumpLicenseCommand.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* 修改 config 后对其 kv 进行排序的操作
|
||||
*/
|
||||
class DumpLicenseCommand extends BaseCommand
|
||||
{
|
||||
protected static $defaultName = 'dump-license';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('Dump licenses for required libraries');
|
||||
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$output->writeln('<info>not implemented</info>');
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
239
src/SPC/command/FetchSourceCommand.php
Normal file
239
src/SPC/command/FetchSourceCommand.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\ExceptionHandler;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\InvalidArgumentException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
use SPC\store\Downloader;
|
||||
use SPC\util\Patcher;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/** @noinspection PhpUnused */
|
||||
class FetchSourceCommand extends BaseCommand
|
||||
{
|
||||
protected static $defaultName = 'fetch';
|
||||
|
||||
protected string $php_major_ver;
|
||||
|
||||
protected InputInterface $input;
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('Fetch required sources');
|
||||
$this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated');
|
||||
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
|
||||
$this->addOption('hash', null, null, 'Hash only');
|
||||
$this->addOption('shallow-clone', null, null, 'Clone shallow');
|
||||
$this->addOption('with-openssl11', null, null, 'Use openssl 1.1');
|
||||
$this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1');
|
||||
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
|
||||
$this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed');
|
||||
}
|
||||
|
||||
public function initialize(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
// --all 等于 "" "",也就是所有东西都要下载
|
||||
if ($input->getOption('all')) {
|
||||
$input->setArgument('extensions', '');
|
||||
$input->setArgument('libraries', '');
|
||||
}
|
||||
parent::initialize($input, $output);
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->input = $input;
|
||||
try {
|
||||
// 匹配版本
|
||||
$ver = $this->php_major_ver = $input->getOption('with-php') ?? '8.1';
|
||||
preg_match('/^\d+\.\d+$/', $ver, $matches);
|
||||
if (!$matches) {
|
||||
logger()->error("bad version arg: {$ver}, x.y required!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 删除旧资源
|
||||
if ($input->getOption('clean')) {
|
||||
logger()->warning('You are doing some operations that not recoverable: removing directories below');
|
||||
logger()->warning(SOURCE_PATH);
|
||||
logger()->warning(DOWNLOAD_PATH);
|
||||
logger()->warning('I will remove these dir after you press [Enter] !');
|
||||
echo 'Confirm operation? [Yes] ';
|
||||
$r = strtolower(trim(fgets(STDIN)));
|
||||
if ($r !== 'yes' && $r !== '') {
|
||||
logger()->notice('Operation canceled.');
|
||||
return 1;
|
||||
}
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . SOURCE_PATH);
|
||||
f_passthru('rmdir /s /q ' . DOWNLOAD_PATH);
|
||||
} else {
|
||||
f_passthru('rm -rf ' . SOURCE_PATH);
|
||||
f_passthru('rm -rf ' . DOWNLOAD_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用浅克隆可以减少调用 git 命令下载资源时的存储空间占用
|
||||
if ($input->getOption('shallow-clone')) {
|
||||
define('GIT_SHALLOW_CLONE', true);
|
||||
}
|
||||
|
||||
// 读取源配置,随便读一个source,用于缓存 source 配置
|
||||
Config::getSource('openssl');
|
||||
|
||||
// 是否启用openssl11
|
||||
if ($input->getOption('with-openssl11')) {
|
||||
logger()->debug('Using openssl 1.1');
|
||||
// 手动修改配置
|
||||
Config::$source['openssl']['regex'] = '/href="(?<file>openssl-(?<version>1.[^"]+)\.tar\.gz)\"/';
|
||||
}
|
||||
|
||||
// 默认预选 phpmicro
|
||||
$chosen_sources = ['micro'];
|
||||
|
||||
// 从参数中获取要编译的 libraries,并转换为数组
|
||||
$libraries = array_map('trim', array_filter(explode(',', $input->getArgument('libraries'))));
|
||||
if ($libraries) {
|
||||
foreach ($libraries as $lib) {
|
||||
// 从 lib 的 config 中找到对应 source 资源名称,组成一个 lib 的 source 列表
|
||||
$src_name = Config::getLib($lib, 'source');
|
||||
$chosen_sources[] = $src_name;
|
||||
}
|
||||
} else { // 如果传入了空串,那么代表 fetch 所有包
|
||||
$chosen_sources = [...$chosen_sources, ...array_map(fn ($x) => $x['source'], array_values(Config::getLibs()))];
|
||||
}
|
||||
|
||||
// 从参数中获取要编译的 extensions,并转换为数组
|
||||
$extensions = array_map('trim', array_filter(explode(',', $input->getArgument('extensions'))));
|
||||
if ($extensions) {
|
||||
foreach ($extensions as $lib) {
|
||||
if (Config::getExt($lib, 'type') !== 'builtin') {
|
||||
$src_name = Config::getExt($lib, 'source');
|
||||
$chosen_sources[] = $src_name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach (Config::getExts() as $ext) {
|
||||
if ($ext['type'] !== 'builtin') {
|
||||
$chosen_sources[] = $ext['source'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$chosen_sources = array_unique($chosen_sources);
|
||||
|
||||
// 是否只hash,不下载资源
|
||||
if ($input->getOption('hash')) {
|
||||
$hash = $this->doHash($chosen_sources);
|
||||
$output->writeln($hash);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 创建目录
|
||||
f_mkdir(SOURCE_PATH);
|
||||
f_mkdir(DOWNLOAD_PATH);
|
||||
|
||||
// 下载 PHP
|
||||
logger()->info('Fetching PHP source');
|
||||
Downloader::fetchSource('php-src', Downloader::getLatestPHPInfo($ver));
|
||||
|
||||
// 下载别的依赖资源
|
||||
$cnt = count($chosen_sources);
|
||||
$ni = 0;
|
||||
foreach ($chosen_sources as $name) {
|
||||
++$ni;
|
||||
logger()->info("Fetching source {$name} [{$ni}/{$cnt}]");
|
||||
Downloader::fetchSource($name, Config::getSource($name));
|
||||
}
|
||||
|
||||
// patch 每份资源只需一次,如果已经下载好的资源已经patch了,就标记一下不patch了
|
||||
if (!file_exists(SOURCE_PATH . '/.patched')) {
|
||||
$this->doPatch();
|
||||
} else {
|
||||
logger()->notice('sources already patched');
|
||||
}
|
||||
|
||||
// 打印拉取资源用时
|
||||
$time = round(microtime(true) - START_TIME, 3);
|
||||
logger()->info('Fetch complete, used ' . $time . ' s !');
|
||||
return 0;
|
||||
} catch (\Throwable $e) {
|
||||
// 不开 debug 模式就不要再显示复杂的调试栈信息了
|
||||
if ($input->getOption('debug')) {
|
||||
ExceptionHandler::getInstance()->handle($e);
|
||||
} else {
|
||||
logger()->emergency($e->getMessage() . ', previous message: ' . $e->getPrevious()->getMessage());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算资源名称列表的 Hash
|
||||
*
|
||||
* @param array $chosen_sources 要计算 hash 的资源名称列表
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DownloaderException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private function doHash(array $chosen_sources): string
|
||||
{
|
||||
$files = [];
|
||||
foreach ($chosen_sources as $name) {
|
||||
$source = Config::getSource($name);
|
||||
$filename = match ($source['type']) {
|
||||
'ghtar' => Downloader::getLatestGithubTarball($name, $source)[1],
|
||||
'ghtagtar' => Downloader::getLatestGithubTarball($name, $source, 'tags')[1],
|
||||
'ghrel' => Downloader::getLatestGithubRelease($name, $source)[1],
|
||||
'filelist' => Downloader::getFromFileList($name, $source)[1],
|
||||
'url' => $source['filename'] ?? basename($source['url']),
|
||||
'git' => null,
|
||||
default => throw new InvalidArgumentException('unknown source type: ' . $source['type']),
|
||||
};
|
||||
if ($filename !== null) {
|
||||
logger()->info("found {$name} source: {$filename}");
|
||||
$files[] = $filename;
|
||||
}
|
||||
}
|
||||
return hash('sha256', implode('|', $files));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在拉回资源后,需要对一些文件做一些补丁 patch
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
private function doPatch(): void
|
||||
{
|
||||
// patch 一些 PHP 的资源,以便编译
|
||||
Patcher::patchPHPDepFiles();
|
||||
|
||||
// openssl 3 需要 patch 额外的东西
|
||||
if (!$this->input->getOption('with-openssl11') && $this->php_major_ver === '8.0') {
|
||||
Patcher::patchOpenssl3();
|
||||
}
|
||||
|
||||
// openssl1.1.1q 在 MacOS 上直接编译会报错,patch 一下
|
||||
// @see: https://github.com/openssl/openssl/issues/18720
|
||||
if ($this->input->getOption('with-openssl11') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c') && PHP_OS_FAMILY === 'Darwin') {
|
||||
Patcher::patchDarwinOpenssl11();
|
||||
}
|
||||
|
||||
// swow 需要软链接内部的文件夹才能正常编译
|
||||
if (!file_exists(SOURCE_PATH . '/php-src/ext/swow')) {
|
||||
Patcher::patchSwow();
|
||||
}
|
||||
|
||||
// 标记 patch 完成,避免重复 patch
|
||||
file_put_contents(SOURCE_PATH . '/.patched', '');
|
||||
}
|
||||
}
|
||||
30
src/SPC/command/ListExtCommand.php
Normal file
30
src/SPC/command/ListExtCommand.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\builder\traits\NoMotdTrait;
|
||||
use SPC\store\Config;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ListExtCommand extends BaseCommand
|
||||
{
|
||||
use NoMotdTrait;
|
||||
|
||||
protected static $defaultName = 'list-ext';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('List supported extensions');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach (Config::getExts() as $ext => $meta) {
|
||||
echo $ext . PHP_EOL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
60
src/SPC/command/SortConfigCommand.php
Normal file
60
src/SPC/command/SortConfigCommand.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\command;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\ValidationException;
|
||||
use SPC\store\FileSystem;
|
||||
use SPC\util\ConfigValidator;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* 修改 config 后对其 kv 进行排序的操作
|
||||
*/
|
||||
class SortConfigCommand extends BaseCommand
|
||||
{
|
||||
protected static $defaultName = 'sort-config';
|
||||
|
||||
public function configure()
|
||||
{
|
||||
$this->setDescription('After config edited, sort it by alphabet');
|
||||
$this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ValidationException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
switch ($name = $input->getArgument('config-name')) {
|
||||
case 'lib':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
|
||||
ConfigValidator::validateLibs($file);
|
||||
ksort($file);
|
||||
file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
break;
|
||||
case 'source':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
|
||||
ConfigValidator::validateSource($file);
|
||||
ksort($file);
|
||||
file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
break;
|
||||
case 'ext':
|
||||
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
|
||||
ConfigValidator::validateExts($file);
|
||||
ksort($file);
|
||||
file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
break;
|
||||
default:
|
||||
$output->writeln("<error>invalid config name: {$name}</error>");
|
||||
return 1;
|
||||
}
|
||||
$output->writeln('<info>sort success</info>');
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
9
src/SPC/exception/DownloaderException.php
Normal file
9
src/SPC/exception/DownloaderException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class DownloaderException extends \Exception
|
||||
{
|
||||
}
|
||||
53
src/SPC/exception/ExceptionHandler.php
Normal file
53
src/SPC/exception/ExceptionHandler.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class ExceptionHandler
|
||||
{
|
||||
protected $whoops;
|
||||
|
||||
private static $obj;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
$whoops_class = 'Whoops\Run';
|
||||
$collision_class = 'NunoMaduro\Collision\Handler';
|
||||
if (class_exists($collision_class) && class_exists($whoops_class)) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
$this->whoops = new $whoops_class();
|
||||
$this->whoops->allowQuit(false);
|
||||
$this->whoops->writeToOutput(false);
|
||||
$this->whoops->pushHandler(new $collision_class());
|
||||
$this->whoops->register();
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance(): ExceptionHandler
|
||||
{
|
||||
if (self::$obj === null) {
|
||||
self::$obj = new self();
|
||||
}
|
||||
return self::$obj;
|
||||
}
|
||||
|
||||
public function getWhoops()
|
||||
{
|
||||
return $this->whoops;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理异常
|
||||
*/
|
||||
public function handle(\Throwable $e): void
|
||||
{
|
||||
if (is_null($this->whoops)) {
|
||||
logger()->error('Uncaught ' . get_class($e) . ': ' . $e->getMessage() . ' at ' . $e->getFile() . '(' . $e->getLine() . ')');
|
||||
logger()->error($e->getTraceAsString());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->whoops->handleException($e);
|
||||
}
|
||||
}
|
||||
9
src/SPC/exception/FileSystemException.php
Normal file
9
src/SPC/exception/FileSystemException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class FileSystemException extends \Exception
|
||||
{
|
||||
}
|
||||
9
src/SPC/exception/InvalidArgumentException.php
Normal file
9
src/SPC/exception/InvalidArgumentException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class InvalidArgumentException extends \Exception
|
||||
{
|
||||
}
|
||||
9
src/SPC/exception/RuntimeException.php
Normal file
9
src/SPC/exception/RuntimeException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class RuntimeException extends \Exception
|
||||
{
|
||||
}
|
||||
9
src/SPC/exception/ValidationException.php
Normal file
9
src/SPC/exception/ValidationException.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\exception;
|
||||
|
||||
class ValidationException extends \Exception
|
||||
{
|
||||
}
|
||||
126
src/SPC/store/Config.php
Normal file
126
src/SPC/store/Config.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* 一个读取 config 配置的操作类
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public static ?array $source = null;
|
||||
|
||||
public static ?array $lib = null;
|
||||
|
||||
public static ?array $ext = null;
|
||||
|
||||
/**
|
||||
* 从配置文件读取一个资源(source)的元信息
|
||||
*
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getSource(string $name): ?array
|
||||
{
|
||||
if (self::$source === null) {
|
||||
self::$source = FileSystem::loadConfigArray('source');
|
||||
}
|
||||
return self::$source[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据不同的操作系统分别选择不同的 lib 库依赖项
|
||||
* 如果 key 为 null,那么直接返回整个 meta。
|
||||
* 如果 key 不为 null,则可以使用的 key 有 static-libs、headers、lib-depends、lib-suggests。
|
||||
* 对于 macOS 平台,支持 frameworks。
|
||||
*
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getLib(string $name, ?string $key = null, mixed $default = null)
|
||||
{
|
||||
if (self::$lib === null) {
|
||||
self::$lib = FileSystem::loadConfigArray('lib');
|
||||
}
|
||||
if (!isset(self::$lib[$name])) {
|
||||
throw new RuntimeException('lib [' . $name . '] is not supported yet for get');
|
||||
}
|
||||
$supported_sys_based = ['static-libs', 'headers', 'lib-depends', 'lib-suggests', 'frameworks'];
|
||||
if ($key !== null && in_array($key, $supported_sys_based)) {
|
||||
$m_key = match (PHP_OS_FAMILY) {
|
||||
'Windows' => ['-windows', '-win', ''],
|
||||
'Darwin' => ['-macos', '-unix', ''],
|
||||
'Linux' => ['-linux', '-unix', ''],
|
||||
default => throw new RuntimeException('OS ' . PHP_OS_FAMILY . ' is not supported'),
|
||||
};
|
||||
foreach ($m_key as $v) {
|
||||
if (isset(self::$lib[$name][$key . $v])) {
|
||||
return self::$lib[$name][$key . $v];
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
if ($key !== null) {
|
||||
return self::$lib[$name][$key] ?? $default;
|
||||
}
|
||||
return self::$lib[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getLibs(): array
|
||||
{
|
||||
if (self::$lib === null) {
|
||||
self::$lib = FileSystem::loadConfigArray('lib');
|
||||
}
|
||||
return self::$lib;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getExt(string $name, ?string $key = null, mixed $default = null)
|
||||
{
|
||||
if (self::$ext === null) {
|
||||
self::$ext = FileSystem::loadConfigArray('ext');
|
||||
}
|
||||
if (!isset(self::$ext[$name])) {
|
||||
throw new RuntimeException('ext [' . $name . '] is not supported yet for get');
|
||||
}
|
||||
$supported_sys_based = ['lib-depends', 'lib-suggests', 'ext-depends', 'ext-suggests', 'arg-type'];
|
||||
if ($key !== null && in_array($key, $supported_sys_based)) {
|
||||
$m_key = match (PHP_OS_FAMILY) {
|
||||
'Windows' => ['-windows', '-win', ''],
|
||||
'Darwin' => ['-macos', '-unix', ''],
|
||||
'Linux' => ['-linux', '-unix', ''],
|
||||
default => throw new RuntimeException('OS ' . PHP_OS_FAMILY . ' is not supported'),
|
||||
};
|
||||
foreach ($m_key as $v) {
|
||||
if (isset(self::$ext[$name][$key . $v])) {
|
||||
return self::$ext[$name][$key . $v];
|
||||
}
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
if ($key !== null) {
|
||||
return self::$ext[$name][$key] ?? $default;
|
||||
}
|
||||
return self::$ext[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getExts(): array
|
||||
{
|
||||
if (self::$ext === null) {
|
||||
self::$ext = FileSystem::loadConfigArray('ext');
|
||||
}
|
||||
return self::$ext;
|
||||
}
|
||||
}
|
||||
31
src/SPC/store/CurlHook.php
Normal file
31
src/SPC/store/CurlHook.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
class CurlHook
|
||||
{
|
||||
/**
|
||||
* 执行 GitHub Token 的 Curl 头添加
|
||||
*
|
||||
* @param string $method 修改的 method
|
||||
* @param string $url 修改的链接
|
||||
* @param array $headers 修改的 headers
|
||||
*/
|
||||
public static function setupGithubToken(string &$method, string &$url, array &$headers): void
|
||||
{
|
||||
if (!getenv('GITHUB_TOKEN')) {
|
||||
return;
|
||||
}
|
||||
if (getenv('GITHUB_USER')) {
|
||||
$auth = base64_encode(getenv('GITHUB_USER') . ':' . getenv('GITHUB_TOKEN'));
|
||||
$headers[] = "Authorization: Basic {$auth}";
|
||||
logger()->info("using basic github token for {$method} {$url}");
|
||||
} else {
|
||||
$auth = getenv('GITHUB_TOKEN');
|
||||
$headers[] = "Authorization: Bearer {$auth}";
|
||||
logger()->info("using bearer github token for {$method} {$url}");
|
||||
}
|
||||
}
|
||||
}
|
||||
348
src/SPC/store/Downloader.php
Normal file
348
src/SPC/store/Downloader.php
Normal file
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use SPC\exception\DownloaderException;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
|
||||
/**
|
||||
* 资源下载器
|
||||
*/
|
||||
class Downloader
|
||||
{
|
||||
/**
|
||||
* 获取 BitBucket 仓库的最新 Tag
|
||||
*
|
||||
* @param string $name 资源名称
|
||||
* @param array $source 资源的元信息,包含字段 repo
|
||||
* @return array<int, string> 返回下载 url 链接和文件名
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function getLatestBitbucketTag(string $name, array $source): array
|
||||
{
|
||||
logger()->debug("finding {$name} source from bitbucket tag");
|
||||
$data = json_decode(self::curlExec(
|
||||
url: "https://api.bitbucket.org/2.0/repositories/{$source['repo']}/refs/tags"
|
||||
), true);
|
||||
$ver = $data['values'][0]['name'];
|
||||
if (!$ver) {
|
||||
throw new DownloaderException("failed to find {$name} bitbucket source");
|
||||
}
|
||||
$url = "https://bitbucket.org/{$source['repo']}/get/{$ver}.tar.gz";
|
||||
$headers = self::curlExec(
|
||||
url: $url,
|
||||
method: 'HEAD'
|
||||
);
|
||||
preg_match('/^content-disposition:\s+attachment;\s*filename=("?)(?<filename>.+\.tar\.gz)\1/im', $headers, $matches);
|
||||
if ($matches) {
|
||||
$filename = $matches['filename'];
|
||||
} else {
|
||||
$filename = "{$name}-{$data['tag_name']}.tar.gz";
|
||||
}
|
||||
|
||||
return [$url, $filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 GitHub 最新的打包地址和文件名
|
||||
*
|
||||
* @param string $name 包名称
|
||||
* @param array $source 源信息
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array
|
||||
{
|
||||
logger()->debug("finding {$name} source from github {$type} tarball");
|
||||
$data = json_decode(self::curlExec(
|
||||
url: "https://api.github.com/repos/{$source['repo']}/{$type}",
|
||||
hooks: [[CurlHook::class, 'setupGithubToken']]
|
||||
), true);
|
||||
$url = $data[0]['tarball_url'];
|
||||
if (!$url) {
|
||||
throw new DownloaderException("failed to find {$name} source");
|
||||
}
|
||||
$headers = self::curlExec(
|
||||
url: $url,
|
||||
method: 'HEAD',
|
||||
hooks: [[CurlHook::class, 'setupGithubToken']],
|
||||
);
|
||||
preg_match('/^content-disposition:\s+attachment;\s*filename=("?)(?<filename>.+\.tar\.gz)\1/im', $headers, $matches);
|
||||
if ($matches) {
|
||||
$filename = $matches['filename'];
|
||||
} else {
|
||||
$filename = "{$name}-" . ($type === 'releases' ? $data['tag_name'] : $data['name']) . '.tar.gz';
|
||||
}
|
||||
|
||||
return [$url, $filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 GitHub 最新的 Release 下载信息
|
||||
*
|
||||
* @param string $name 资源名
|
||||
* @param array $source 资源的元信息,包含字段 repo、match
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function getLatestGithubRelease(string $name, array $source): array
|
||||
{
|
||||
logger()->debug("finding {$name} source from github releases assests");
|
||||
$data = json_decode(self::curlExec(
|
||||
url: "https://api.github.com/repos/{$source['repo']}/releases",
|
||||
hooks: [[CurlHook::class, 'setupGithubToken']],
|
||||
), true);
|
||||
$url = null;
|
||||
foreach ($data[0]['assets'] as $asset) {
|
||||
if (preg_match('|' . $source['match'] . '|', $asset['name'])) {
|
||||
$url = $asset['browser_download_url'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$url) {
|
||||
throw new DownloaderException("failed to find {$name} source");
|
||||
}
|
||||
$filename = basename($url);
|
||||
|
||||
return [$url, $filename];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件列表的资源链接和名称
|
||||
*
|
||||
* @param string $name 资源名称
|
||||
* @param array $source 资源元信息,包含 url、regex
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function getFromFileList(string $name, array $source): array
|
||||
{
|
||||
logger()->debug("finding {$name} source from file list");
|
||||
$page = self::curlExec($source['url']);
|
||||
preg_match_all($source['regex'], $page, $matches);
|
||||
if (!$matches) {
|
||||
throw new DownloaderException("Failed to get {$name} version");
|
||||
}
|
||||
$versions = [];
|
||||
foreach ($matches['version'] as $i => $version) {
|
||||
$lowerVersion = strtolower($version);
|
||||
foreach ([
|
||||
'alpha',
|
||||
'beta',
|
||||
'rc',
|
||||
'pre',
|
||||
'nightly',
|
||||
'snapshot',
|
||||
'dev',
|
||||
] as $betaVersion) {
|
||||
if (str_contains($lowerVersion, $betaVersion)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$versions[$version] = $matches['file'][$i];
|
||||
}
|
||||
uksort($versions, 'version_compare');
|
||||
|
||||
return [$source['url'] . end($versions), end($versions)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过链接下载资源到本地并解压
|
||||
*
|
||||
* @param string $name 资源名称
|
||||
* @param string $url 下载链接
|
||||
* @param string $filename 下载到下载目录的目标文件名称,例如 xz.tar.gz
|
||||
* @param null|string $path 如果指定了此参数,则会移动该资源目录到目标目录
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function downloadUrl(string $name, string $url, string $filename, ?string $path = null): void
|
||||
{
|
||||
if (!file_exists(DOWNLOAD_PATH . "/{$filename}")) {
|
||||
logger()->debug("downloading {$url}");
|
||||
self::curlDown(url: $url, path: DOWNLOAD_PATH . "/{$filename}");
|
||||
} else {
|
||||
logger()->notice("{$filename} already exists");
|
||||
}
|
||||
FileSystem::extractSource($name, DOWNLOAD_PATH . "/{$filename}");
|
||||
if ($path) {
|
||||
$path = FileSystem::convertPath(SOURCE_PATH . "/{$path}");
|
||||
$src_path = FileSystem::convertPath(SOURCE_PATH . "/{$name}");
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
f_passthru('move "' . $src_path . '" "' . $path . '"');
|
||||
break;
|
||||
case 'Linux':
|
||||
case 'Darwin':
|
||||
f_passthru('mv "' . $src_path . '" "' . $path . '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function downloadGit(string $name, string $url, string $branch, ?string $path = null): void
|
||||
{
|
||||
if ($path) {
|
||||
$path = SOURCE_PATH . "/{$path}";
|
||||
} else {
|
||||
$path = DOWNLOAD_PATH . "/{$name}";
|
||||
}
|
||||
$download_path = DOWNLOAD_PATH . "/{$name}";
|
||||
if (file_exists($download_path)) {
|
||||
logger()->notice("{$name} git source already fetched");
|
||||
} else {
|
||||
logger()->debug("cloning {$name} source");
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
f_passthru(
|
||||
'git clone' . $check .
|
||||
' --config core.autocrlf=false ' .
|
||||
"--branch \"{$branch}\" " . (defined('GIT_SHALLOW_CLONE') ? '--depth 1 --single-branch' : '') . " --recursive \"{$url}\" \"{$download_path}\""
|
||||
);
|
||||
}
|
||||
// 复制目录过去
|
||||
if ($path !== $download_path) {
|
||||
$dst_path = FileSystem::convertPath($path);
|
||||
$src_path = FileSystem::convertPath($download_path);
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
f_passthru('xcopy "' . $src_path . '" "' . $dst_path . '" /s/e/v/y/i');
|
||||
break;
|
||||
case 'Linux':
|
||||
case 'Darwin':
|
||||
f_passthru('cp -r "' . $src_path . '" "' . $dst_path . '"');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拉取资源
|
||||
*
|
||||
* @param string $name 资源名称
|
||||
* @param array $source 资源参数,包含 type、path、rev、url、filename、regex、license
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function fetchSource(string $name, array $source): void
|
||||
{
|
||||
// 避免重复 fetch
|
||||
if (is_dir(FileSystem::convertPath(SOURCE_PATH . "/{$name}")) || isset($source['path']) && is_dir(FileSystem::convertPath(SOURCE_PATH . "/{$source['path']}"))) {
|
||||
logger()->notice("{$name} source already extracted");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
switch ($source['type']) {
|
||||
case 'bitbuckettag': // 从 BitBucket 的 Tag 拉取
|
||||
[$url, $filename] = self::getLatestBitbucketTag($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghtar': // 从 GitHub 的 TarBall 拉取
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghtagtar': // 根据 GitHub 的 Tag 拉取相应版本的 Tar
|
||||
[$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags');
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'ghrel': // 通过 GitHub Release 来拉取
|
||||
[$url, $filename] = self::getLatestGithubRelease($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'filelist': // 通过网站提供的 filelist 使用正则提取后拉取
|
||||
[$url, $filename] = self::getFromFileList($name, $source);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'url': // 通过直链拉取
|
||||
$url = $source['url'];
|
||||
$filename = $source['filename'] ?? basename($source['url']);
|
||||
self::downloadUrl($name, $url, $filename, $source['path'] ?? null);
|
||||
break;
|
||||
case 'git': // 通过拉回 Git 仓库的形式拉取
|
||||
self::downloadGit($name, $source['url'], $source['rev'], $source['path'] ?? null);
|
||||
break;
|
||||
default:
|
||||
throw new DownloaderException('unknown source type: ' . $source['type']);
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
// 因为某些时候通过命令行下载的文件在失败后不会删除,这里检测到文件存在需要手动删一下
|
||||
if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) {
|
||||
logger()->warning('Deleting download file: ' . $filename);
|
||||
unlink(DOWNLOAD_PATH . '/' . $filename);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 PHP x.y 的具体版本号,例如通过 8.1 来获取 8.1.10
|
||||
*
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
#[ArrayShape(['type' => 'string', 'path' => 'string', 'rev' => 'string', 'url' => 'string'])]
|
||||
public static function getLatestPHPInfo(string $major_version): array
|
||||
{
|
||||
// 查找最新的小版本号
|
||||
$info = json_decode(self::curlExec(url: "https://www.php.net/releases/index.php?json&version={$major_version}"), true);
|
||||
$version = $info['version'];
|
||||
|
||||
// 从官网直接下载
|
||||
return [
|
||||
'type' => 'url',
|
||||
'url' => "https://www.php.net/distributions/php-{$version}.tar.gz",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 curl 命令拉取元信息
|
||||
*
|
||||
* @throws DownloaderException
|
||||
*/
|
||||
public static function curlExec(string $url, string $method = 'GET', array $headers = [], array $hooks = []): string
|
||||
{
|
||||
foreach ($hooks as $hook) {
|
||||
$hook($method, $url, $headers);
|
||||
}
|
||||
|
||||
FileSystem::findCommandPath('curl');
|
||||
|
||||
$methodArg = match ($method) {
|
||||
'GET' => '',
|
||||
'HEAD' => '-I',
|
||||
default => "-X \"{$method}\"",
|
||||
};
|
||||
$headerArg = implode(' ', array_map(fn ($v) => '"-H' . $v . '"', $headers));
|
||||
|
||||
$cmd = "curl -sfSL {$methodArg} {$headerArg} \"{$url}\"";
|
||||
f_exec($cmd, $output, $ret);
|
||||
if ($ret !== 0) {
|
||||
throw new DownloaderException('failed http fetch');
|
||||
}
|
||||
return implode("\n", $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 curl 命令下载文件
|
||||
*
|
||||
* @throws DownloaderException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function curlDown(string $url, string $path, string $method = 'GET', array $headers = [], array $hooks = []): void
|
||||
{
|
||||
foreach ($hooks as $hook) {
|
||||
$hook($method, $url, $headers);
|
||||
}
|
||||
|
||||
$methodArg = match ($method) {
|
||||
'GET' => '',
|
||||
'HEAD' => '-I',
|
||||
default => "-X \"{$method}\"",
|
||||
};
|
||||
$headerArg = implode(' ', array_map(fn ($v) => '"-H' . $v . '"', $headers));
|
||||
$check = !defined('DEBUG_MODE') ? 's' : '#';
|
||||
$cmd = "curl -{$check}fSL -o \"{$path}\" {$methodArg} {$headerArg} \"{$url}\"";
|
||||
f_passthru($cmd);
|
||||
}
|
||||
}
|
||||
381
src/SPC/store/FileSystem.php
Normal file
381
src/SPC/store/FileSystem.php
Normal file
@@ -0,0 +1,381 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\store;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator;
|
||||
|
||||
class FileSystem
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function loadConfigArray(string $config): array
|
||||
{
|
||||
$whitelist = ['ext', 'lib', 'source'];
|
||||
if (!in_array($config, $whitelist)) {
|
||||
throw new FileSystemException('Reading ' . $config . '.json is not allowed');
|
||||
}
|
||||
$tries = [
|
||||
WORKING_DIR . '/config/' . $config . '.json',
|
||||
ROOT_DIR . '/config/' . $config . '.json',
|
||||
];
|
||||
foreach ($tries as $try) {
|
||||
if (file_exists($try)) {
|
||||
$json = json_decode(self::readFile($try), true);
|
||||
if (!is_array($json)) {
|
||||
throw new FileSystemException('Reading ' . $try . ' failed');
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
throw new FileSystemException('Reading ' . $config . '.json failed');
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件,读不出来直接抛出异常
|
||||
*
|
||||
* @param string $filename 文件路径
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function readFile(string $filename): string
|
||||
{
|
||||
// logger()->debug('Reading file: ' . $filename);
|
||||
$r = file_get_contents(self::convertPath($filename));
|
||||
if ($r === false) {
|
||||
throw new FileSystemException('Reading file ' . $filename . ' failed');
|
||||
}
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): bool|int
|
||||
{
|
||||
logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename);
|
||||
$file = self::readFile($filename);
|
||||
switch ($replace_type) {
|
||||
case REPLACE_FILE_STR:
|
||||
default:
|
||||
$file = str_replace($callback_or_search, $to_replace, $file);
|
||||
break;
|
||||
case REPLACE_FILE_PREG:
|
||||
$file = preg_replace($callback_or_search, $to_replace, $file);
|
||||
break;
|
||||
case REPLACE_FILE_USER:
|
||||
$file = $callback_or_search($file);
|
||||
break;
|
||||
}
|
||||
return file_put_contents($filename, $file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀
|
||||
*
|
||||
* @param string $fn 文件名
|
||||
*/
|
||||
public static function extname(string $fn): string
|
||||
{
|
||||
$parts = explode('.', basename($fn));
|
||||
if (count($parts) < 2) {
|
||||
return '';
|
||||
}
|
||||
return array_pop($parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* 寻找命令的真实路径,效果类似 which
|
||||
*
|
||||
* @param string $name 命令名称
|
||||
* @param array $paths 路径列表,如果为空则默认从 PATH 系统变量搜索
|
||||
*/
|
||||
public static function findCommandPath(string $name, array $paths = []): ?string
|
||||
{
|
||||
if (!$paths) {
|
||||
$paths = explode(PATH_SEPARATOR, getenv('PATH'));
|
||||
}
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
foreach ($paths as $path) {
|
||||
foreach (['.exe', '.bat', '.cmd'] as $suffix) {
|
||||
if (file_exists($path . DIRECTORY_SEPARATOR . $name . $suffix)) {
|
||||
return $path . DIRECTORY_SEPARATOR . $name . $suffix;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path . DIRECTORY_SEPARATOR . $name)) {
|
||||
return $path . DIRECTORY_SEPARATOR . $name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function copyDir(string $from, string $to): void
|
||||
{
|
||||
$iterator = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($from, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_FILEINFO), \RecursiveIteratorIterator::SELF_FIRST);
|
||||
foreach ($iterator as $item) {
|
||||
/**
|
||||
* @var \SplFileInfo $item
|
||||
*/
|
||||
$target = $to . substr($item->getPathname(), strlen($from));
|
||||
if ($item->isDir()) {
|
||||
logger()->info("mkdir {$target}");
|
||||
f_mkdir($target, recursive: true);
|
||||
} else {
|
||||
logger()->info("copying {$item} to {$target}");
|
||||
@f_mkdir(dirname($target), recursive: true);
|
||||
copy($item->getPathname(), $target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压缩下载的资源包到 source 目录
|
||||
*
|
||||
* @param string $name 资源名
|
||||
* @param string $filename 文件名
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function extractSource(string $name, string $filename): void
|
||||
{
|
||||
logger()->info("extracting {$name} source");
|
||||
try {
|
||||
if (PHP_OS_FAMILY !== 'Windows') {
|
||||
if (f_mkdir(directory: SOURCE_PATH . "/{$name}", recursive: true) !== true) {
|
||||
throw new FileSystemException('create ' . $name . 'source dir failed');
|
||||
}
|
||||
switch (self::extname($filename)) {
|
||||
case 'xz':
|
||||
case 'txz':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
// f_passthru("cat {$filename} | xz -d | tar -x -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
f_passthru("tar -xzf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'bz2':
|
||||
f_passthru("tar -xjf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("unzip {$filename} -d " . SOURCE_PATH . "/{$name}");
|
||||
break;
|
||||
// case 'zstd':
|
||||
// case 'zst':
|
||||
// passthru('cat ' . $filename . ' | zstd -d | tar -x -C ".SOURCE_PATH . "/' . $name . ' --strip-components 1', $ret);
|
||||
// break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException('unknown archive format: ' . $filename);
|
||||
}
|
||||
} else {
|
||||
// find 7z
|
||||
$_7zExe = self::findCommandPath('7z', [
|
||||
'C:\Program Files\7-Zip-Zstandard',
|
||||
'C:\Program Files (x86)\7-Zip-Zstandard',
|
||||
'C:\Program Files\7-Zip',
|
||||
'C:\Program Files (x86)\7-Zip',
|
||||
]);
|
||||
if (!$_7zExe) {
|
||||
throw new FileSystemException('windows needs 7z to unpack');
|
||||
}
|
||||
f_mkdir(SOURCE_PATH . "/{$name}", recursive: true);
|
||||
switch (self::extname($filename)) {
|
||||
case 'zstd':
|
||||
case 'zst':
|
||||
if (!str_contains($_7zExe, 'Zstandard')) {
|
||||
throw new FileSystemException("zstd is not supported: {$filename}");
|
||||
}
|
||||
// no break
|
||||
case 'xz':
|
||||
case 'txz':
|
||||
case 'gz':
|
||||
case 'tgz':
|
||||
case 'bz2':
|
||||
f_passthru("\"{$_7zExe}\" x -so {$filename} | tar -f - -x -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'tar':
|
||||
f_passthru("tar -xf {$filename} -C " . SOURCE_PATH . "/{$name} --strip-components 1");
|
||||
break;
|
||||
case 'zip':
|
||||
f_passthru("\"{$_7zExe}\" x {$filename} -o" . SOURCE_PATH . "/{$name}");
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException("unknown archive format: {$filename}");
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException $e) {
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('rmdir /s /q ' . SOURCE_PATH . "/{$name}");
|
||||
} else {
|
||||
f_passthru('rm -r ' . SOURCE_PATH . "/{$name}");
|
||||
}
|
||||
throw new FileSystemException('Cannot extract source ' . $name, $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据系统环境的不同,自动转换路径的分隔符
|
||||
*
|
||||
* @param string $path 路径
|
||||
*/
|
||||
public static function convertPath(string $path): string
|
||||
{
|
||||
if (str_starts_with($path, 'phar://')) {
|
||||
return $path;
|
||||
}
|
||||
return str_replace('/', DIRECTORY_SEPARATOR, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表
|
||||
*
|
||||
* @param string $dir 目录
|
||||
* @param bool $recursive 是否递归扫描子目录
|
||||
* @param bool|string $relative 是否返回相对目录,如果为true则返回相对目录,如果为false则返回绝对目录
|
||||
* @param bool $include_dir 非递归模式下,是否包含目录
|
||||
* @return array|false
|
||||
* @since 2.5
|
||||
*/
|
||||
public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): bool|array
|
||||
{
|
||||
$dir = self::convertPath($dir);
|
||||
// 不是目录不扫,直接 false 处理
|
||||
if (!is_dir($dir)) {
|
||||
logger()->warning('Scan dir failed, no such directory.');
|
||||
return false;
|
||||
}
|
||||
logger()->debug('scanning directory ' . $dir);
|
||||
// 套上 zm_dir
|
||||
$scan_list = scandir($dir);
|
||||
if ($scan_list === false) {
|
||||
logger()->warning('Scan dir failed, cannot scan directory: ' . $dir);
|
||||
return false;
|
||||
}
|
||||
$list = [];
|
||||
// 将 relative 置为相对目录的前缀
|
||||
if ($relative === true) {
|
||||
$relative = $dir;
|
||||
}
|
||||
// 遍历目录
|
||||
foreach ($scan_list as $v) {
|
||||
// Unix 系统排除这俩目录
|
||||
if ($v == '.' || $v == '..') {
|
||||
continue;
|
||||
}
|
||||
$sub_file = self::convertPath($dir . '/' . $v);
|
||||
if (is_dir($sub_file) && $recursive) {
|
||||
# 如果是 目录 且 递推 , 则递推添加下级文件
|
||||
$list = array_merge($list, self::scanDirFiles($sub_file, $recursive, $relative));
|
||||
} elseif (is_file($sub_file) || is_dir($sub_file) && !$recursive && $include_dir) {
|
||||
# 如果是 文件 或 (是 目录 且 不递推 且 包含目录)
|
||||
if (is_string($relative) && mb_strpos($sub_file, $relative) === 0) {
|
||||
$list[] = ltrim(mb_substr($sub_file, mb_strlen($relative)), '/\\');
|
||||
} elseif ($relative === false) {
|
||||
$list[] = $sub_file;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取该路径下的所有类名,根据 psr-4 方式
|
||||
*
|
||||
* @param string $dir 目录
|
||||
* @param string $base_namespace 基类命名空间
|
||||
* @param null|mixed $rule 规则回调
|
||||
* @param bool|string $return_path_value 是否返回路径对应的数组,默认只返回类名列表
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function getClassesPsr4(string $dir, string $base_namespace, mixed $rule = null, bool|string $return_path_value = false): array
|
||||
{
|
||||
$classes = [];
|
||||
// 扫描目录,使用递归模式,相对路径模式,因为下面此路径要用作转换成namespace
|
||||
$files = FileSystem::scanDirFiles($dir, true, true);
|
||||
if ($files === false) {
|
||||
throw new FileSystemException('Cannot scan dir files during get classes psr-4 from dir: ' . $dir);
|
||||
}
|
||||
foreach ($files as $v) {
|
||||
$pathinfo = pathinfo($v);
|
||||
if (($pathinfo['extension'] ?? '') == 'php') {
|
||||
$path = rtrim($dir, '/') . '/' . rtrim($pathinfo['dirname'], './') . '/' . $pathinfo['basename'];
|
||||
|
||||
// 过滤不包含类的文件
|
||||
$tokens = token_get_all(self::readFile($path));
|
||||
$found = false;
|
||||
foreach ($tokens as $token) {
|
||||
if (!is_array($token)) {
|
||||
continue;
|
||||
}
|
||||
if ($token[0] === T_CLASS) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($rule === null) { // 规则未设置回调时候,使用默认的识别过滤规则
|
||||
/*if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') {
|
||||
continue;
|
||||
}*/
|
||||
if (file_exists($dir . '/' . $pathinfo['basename'] . '.ignore')) {
|
||||
continue;
|
||||
}
|
||||
if (mb_substr($pathinfo['basename'], 0, 7) == 'global_' || mb_substr($pathinfo['basename'], 0, 7) == 'script_') {
|
||||
continue;
|
||||
}
|
||||
} elseif (is_callable($rule) && !$rule($dir, $pathinfo)) {
|
||||
continue;
|
||||
}
|
||||
$dirname = $pathinfo['dirname'] == '.' ? '' : (str_replace('/', '\\', $pathinfo['dirname']) . '\\');
|
||||
$class_name = $base_namespace . '\\' . $dirname . $pathinfo['filename'];
|
||||
if (is_string($return_path_value)) {
|
||||
$classes[$class_name] = $return_path_value . '/' . $v;
|
||||
} else {
|
||||
$classes[] = $class_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除目录及目录下的所有文件(危险操作)
|
||||
*
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function removeDir(string $dir, bool $throw_on_fail = false): bool
|
||||
{
|
||||
$dir = FileSystem::convertPath($dir);
|
||||
logger()->warning('Removing path recursively: "' . $dir . '"');
|
||||
switch (PHP_OS_FAMILY) {
|
||||
case 'Windows':
|
||||
case 'WINNT':
|
||||
case 'Cygwin':
|
||||
f_exec('rmdir /s /g "' . $dir . '"', $out, $ret);
|
||||
break;
|
||||
case 'Darwin':
|
||||
case 'Linux':
|
||||
f_exec('rm -rf ' . escapeshellarg($dir), $out, $ret);
|
||||
break;
|
||||
default:
|
||||
throw new FileSystemException('Unsupported OS type: ' . PHP_OS_FAMILY);
|
||||
}
|
||||
if ($ret !== 0 && $throw_on_fail) {
|
||||
throw new FileSystemException('Cannot remove dir "' . $dir . '"');
|
||||
}
|
||||
return $ret === 0;
|
||||
}
|
||||
}
|
||||
71
src/SPC/util/ConfigValidator.php
Normal file
71
src/SPC/util/ConfigValidator.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\exception\ValidationException;
|
||||
|
||||
class ConfigValidator
|
||||
{
|
||||
/**
|
||||
* 验证 source.json
|
||||
*
|
||||
* @param array $data source.json 加载后的数据
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function validateSource(array $data): void
|
||||
{
|
||||
foreach ($data as $name => $src) {
|
||||
isset($src['type']) || throw new ValidationException("source {$name} must have prop: [type]");
|
||||
is_string($src['type']) || throw new ValidationException("source {$name} type prop must be string");
|
||||
in_array($src['type'], ['filelist', 'git', 'ghtagtar', 'ghtar', 'ghrel', 'url']) || throw new ValidationException("source {$name} type [{$src['type']}] is invalid");
|
||||
switch ($src['type']) {
|
||||
case 'filelist':
|
||||
isset($src['url'], $src['regex']) || throw new ValidationException("source {$name} needs [url] and [regex] props");
|
||||
is_string($src['url']) && is_string($src['regex']) || throw new ValidationException("source {$name} [url] and [regex] must be string");
|
||||
break;
|
||||
case 'git':
|
||||
isset($src['url'], $src['rev']) || throw new ValidationException("source {$name} needs [url] and [rev] props");
|
||||
is_string($src['url']) && is_string($src['rev']) || throw new ValidationException("source {$name} [url] and [rev] must be string");
|
||||
is_string($src['path'] ?? '') || throw new ValidationException("source {$name} [path] must be string");
|
||||
break;
|
||||
case 'ghtagtar':
|
||||
case 'ghtar':
|
||||
isset($src['repo']) || throw new ValidationException("source {$name} needs [repo] prop");
|
||||
is_string($src['repo']) || throw new ValidationException("source {$name} [repo] must be string");
|
||||
is_string($src['path'] ?? '') || throw new ValidationException("source {$name} [path] must be string");
|
||||
break;
|
||||
case 'ghrel':
|
||||
isset($src['repo'], $src['match']) || throw new ValidationException("source {$name} needs [repo] and [match] props");
|
||||
is_string($src['repo']) && is_string($src['match']) || throw new ValidationException("source {$name} [repo] and [match] must be string");
|
||||
break;
|
||||
case 'url':
|
||||
isset($src['url']) || throw new ValidationException("source {$name} needs [url] prop");
|
||||
is_string($src['url']) || throw new ValidationException("source {$name} [url] must be string");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function validateLibs($data, array $source_data = []): void
|
||||
{
|
||||
is_array($data) || throw new ValidationException('lib.json is broken');
|
||||
foreach ($data as $name => $lib) {
|
||||
isset($lib['source']) || throw new ValidationException("lib {$name} does not assign any source");
|
||||
is_string($lib['source']) || throw new ValidationException("lib {$name} source must be string");
|
||||
empty($source_data) || isset($source_data[$lib['source']]) || throw new ValidationException("lib {$name} assigns an invalid source: {$lib['source']}");
|
||||
!isset($lib['lib-depends']) || !is_assoc_array($lib['lib-depends']) || throw new ValidationException("lib {$name} dependencies must be a list");
|
||||
!isset($lib['lib-suggests']) || !is_assoc_array($lib['lib-suggests']) || throw new ValidationException("lib {$name} suggested dependencies must be a list");
|
||||
}
|
||||
}
|
||||
|
||||
public static function validateExts($data, array $source_data = []): void
|
||||
{
|
||||
is_array($data) || throw new ValidationException('ext.json is broken');
|
||||
}
|
||||
}
|
||||
106
src/SPC/util/DependencyUtil.php
Normal file
106
src/SPC/util/DependencyUtil.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\Config;
|
||||
|
||||
/**
|
||||
* 依赖处理工具类,包含处理扩展、库的依赖列表顺序等
|
||||
*/
|
||||
class DependencyUtil
|
||||
{
|
||||
/**
|
||||
* 根据需要的 ext 列表获取依赖的 lib 列表,同时根据依赖关系排序
|
||||
*
|
||||
* @param array $exts 要获取 libs 依赖的列表
|
||||
* @param array $additional_libs 额外要添加的库列表,用于激活 lib-suggests 触发的额外库特性
|
||||
* @return array 返回一个包含三个数组的数组,第一个是排序后的 ext 列表,第二个是排序后的 lib 列表,第三个是没有传入但是依赖了的 ext 列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array
|
||||
{
|
||||
// 先对扩展列表进行一个依赖筛选
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
$not_included_exts = [];
|
||||
foreach ($exts as $ext) {
|
||||
if (!isset($visited[$ext])) {
|
||||
self::visitExtDeps($ext, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
$libs = $additional_libs;
|
||||
// 遍历每一个 ext 的 libs
|
||||
foreach ($sorted as $ext) {
|
||||
if (!in_array($ext, $exts)) {
|
||||
$not_included_exts[] = $ext;
|
||||
}
|
||||
foreach (Config::getExt($ext, 'lib-depends', []) as $lib) {
|
||||
if (!in_array($lib, $libs)) {
|
||||
$libs[] = $lib;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [$sorted, self::getLibsByDeps($libs), $not_included_exts];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 lib 库的依赖关系进行一个排序,同时返回多出来的依赖列表
|
||||
*
|
||||
* @param array $libs 要排序的 libs 列表
|
||||
* @return array 排序后的列表
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function getLibsByDeps(array $libs): array
|
||||
{
|
||||
$sorted = [];
|
||||
$visited = [];
|
||||
|
||||
// 遍历所有
|
||||
foreach ($libs as $lib) {
|
||||
if (!isset($visited[$lib])) {
|
||||
self::visitLibDeps($lib, $visited, $sorted);
|
||||
}
|
||||
}
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private static function visitLibDeps(string $lib_name, array &$visited, array &$sorted): void
|
||||
{
|
||||
// 如果已经识别到了,那就不管
|
||||
if (isset($visited[$lib_name])) {
|
||||
return;
|
||||
}
|
||||
$visited[$lib_name] = true;
|
||||
// 遍历该依赖的所有依赖(此处的 getLib 如果检测到当前库不存在的话,会抛出异常)
|
||||
foreach (Config::getLib($lib_name, 'lib-depends', []) as $dep) {
|
||||
self::visitLibDeps($dep, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $lib_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
private static function visitExtDeps(string $ext_name, array &$visited, array &$sorted): void
|
||||
{
|
||||
if (isset($visited[$ext_name])) {
|
||||
return;
|
||||
}
|
||||
$visited[$ext_name] = true;
|
||||
foreach (Config::getExt($ext_name, 'ext-depends', []) as $dep) {
|
||||
self::visitExtDeps($dep, $visited, $sorted);
|
||||
}
|
||||
$sorted[] = $ext_name;
|
||||
}
|
||||
}
|
||||
271
src/SPC/util/Patcher.php
Normal file
271
src/SPC/util/Patcher.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SPC\util;
|
||||
|
||||
use SPC\builder\BuilderBase;
|
||||
use SPC\builder\macos\MacOSBuilder;
|
||||
use SPC\exception\FileSystemException;
|
||||
use SPC\exception\RuntimeException;
|
||||
use SPC\store\FileSystem;
|
||||
|
||||
class Patcher
|
||||
{
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchPHPDepFiles(): void
|
||||
{
|
||||
$ver_file = SOURCE_PATH . '/php-src/main/php_version.h';
|
||||
if (!file_exists($ver_file)) {
|
||||
throw new FileSystemException('Patch failed, cannot find php source files');
|
||||
}
|
||||
$version_h = FileSystem::readFile(SOURCE_PATH . '/php-src/main/php_version.h');
|
||||
preg_match('/#\s*define\s+PHP_MAJOR_VERSION\s+(\d+)\s+#\s*define\s+PHP_MINOR_VERSION\s+(\d+)\s+/m', $version_h, $match);
|
||||
// $ver = "{$match[1]}.{$match[2]}";
|
||||
|
||||
logger()->info('Patching php');
|
||||
|
||||
$major_ver = $match[1] . $match[2];
|
||||
$check = !defined('DEBUG_MODE') ? ' -q' : '';
|
||||
// f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD');
|
||||
|
||||
$patch_list = [
|
||||
'static_opcache',
|
||||
'static_extensions_win32',
|
||||
'cli_checks',
|
||||
'disable_huge_page',
|
||||
'vcruntime140',
|
||||
'win32',
|
||||
'zend_stream',
|
||||
];
|
||||
$patch_list = array_merge($patch_list, match (PHP_OS_FAMILY) {
|
||||
'Windows' => [
|
||||
'cli_static',
|
||||
],
|
||||
'Darwin' => [
|
||||
'macos_iconv',
|
||||
],
|
||||
default => [],
|
||||
});
|
||||
$patches = [];
|
||||
$serial = ['80', '81', '82'];
|
||||
foreach ($patch_list as $patchName) {
|
||||
if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) {
|
||||
$patches[] = "sapi/micro/patches/{$patchName}.patch";
|
||||
continue;
|
||||
}
|
||||
for ($i = array_search($major_ver, $serial, true); $i >= 0; --$i) {
|
||||
$tryMajMin = $serial[$i];
|
||||
if (!file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}_{$tryMajMin}.patch")) {
|
||||
continue;
|
||||
}
|
||||
$patches[] = "sapi/micro/patches/{$patchName}_{$tryMajMin}.patch";
|
||||
continue 2;
|
||||
}
|
||||
throw new RuntimeException("failed finding patch {$patchName}");
|
||||
}
|
||||
|
||||
$patchesStr = str_replace('/', DIRECTORY_SEPARATOR, implode(' ', $patches));
|
||||
|
||||
f_passthru(
|
||||
'cd ' . SOURCE_PATH . '/php-src && ' .
|
||||
(PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patchesStr . ' | patch -p1'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchOpenssl3(): void
|
||||
{
|
||||
logger()->info('Patching PHP with openssl 3.0');
|
||||
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
|
||||
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchSwow(): void
|
||||
{
|
||||
logger()->info('Patching swow');
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
|
||||
} else {
|
||||
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow');
|
||||
}
|
||||
}
|
||||
|
||||
public static function patchPHPBeforeConfigure(BuilderBase $builder): void
|
||||
{
|
||||
if ($builder->getExt('curl')) {
|
||||
logger()->info('patching before-configure for curl checks');
|
||||
$file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])";
|
||||
$files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4');
|
||||
$file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])';
|
||||
file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2);
|
||||
}
|
||||
|
||||
// if ($builder->getExt('pdo_sqlite')) {
|
||||
// FileSystem::replaceFile()
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchPHPConfigure(BuilderBase $builder): void
|
||||
{
|
||||
$frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : '';
|
||||
$curl = $builder->getExt('curl');
|
||||
if ($curl) {
|
||||
logger()->info('patching configure for curl checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lcurl/',
|
||||
$curl->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$bzip2 = $builder->getExt('bz2');
|
||||
if ($bzip2) {
|
||||
logger()->info('patching configure for bzip2 checks');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/-lbz2/',
|
||||
$bzip2->getLibFilesString() . $frameworks
|
||||
);
|
||||
}
|
||||
$pdo_sqlite = $builder->getExt('pdo_sqlite');
|
||||
if ($pdo_sqlite) {
|
||||
logger()->info('patching configure for pdo_sqlite linking');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/sqlite3_column_table_name=yes/',
|
||||
'sqlite3_column_table_name=no'
|
||||
);
|
||||
}
|
||||
logger()->info('patching configure for disable capstone');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_capstone="yes"/',
|
||||
'have_capstone="no"'
|
||||
);
|
||||
if (property_exists($builder, 'arch') && php_uname('m') !== $builder->arch) {
|
||||
// cross-compiling
|
||||
switch ($builder->arch) {
|
||||
case 'aarch64':
|
||||
case 'arm64':
|
||||
// almost all bsd/linux supports this
|
||||
logger()->info('patching configure for shm mmap checks (cross-compiling)');
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_anon=no/',
|
||||
'have_shm_mmap_anon=yes'
|
||||
);
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/php-src/configure',
|
||||
REPLACE_FILE_PREG,
|
||||
'/have_shm_mmap_posix=no/',
|
||||
'have_shm_mmap_posix=yes'
|
||||
);
|
||||
break;
|
||||
case 'x86_64':
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('unsupported arch: ' . $builder->arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchUnixLibpng(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/libpng/configure',
|
||||
REPLACE_FILE_STR,
|
||||
'-lz',
|
||||
BUILD_LIB_PATH . '/libz.a'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
*/
|
||||
public static function patchDarwinOpenssl11(): void
|
||||
{
|
||||
FileSystem::replaceFile(
|
||||
SOURCE_PATH . '/openssl/test/v3ext.c',
|
||||
REPLACE_FILE_STR,
|
||||
'#include <stdio.h>',
|
||||
'#include <stdio.h>' . PHP_EOL . '#include <string.h>'
|
||||
);
|
||||
}
|
||||
|
||||
public static function patchLinuxPkgConfig(string $path): void
|
||||
{
|
||||
logger()->info("fixing pc {$path}");
|
||||
|
||||
$workspace = BUILD_ROOT_PATH;
|
||||
if ($workspace === '/') {
|
||||
$workspace = '';
|
||||
}
|
||||
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('/^prefix=.+$/m', "prefix={$workspace}", $content);
|
||||
$content = preg_replace('/^libdir=.+$/m', 'libdir=${prefix}/lib', $content);
|
||||
$content = preg_replace('/^includedir=.+$/m', 'includedir=${prefix}/include', $content);
|
||||
file_put_contents($path, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FileSystemException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public static function patchLinuxConfigHeader(string $libc): void
|
||||
{
|
||||
switch ($libc) {
|
||||
case 'musl_wrapper':
|
||||
// bad checks
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', '');
|
||||
// no break
|
||||
case 'musl':
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_FUNC_ATTRIBUTE_IFUNC 1$/m', '');
|
||||
break;
|
||||
case 'glibc':
|
||||
// avoid lcrypt dependency
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_R 1$/m', '');
|
||||
FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_H 1$/m', '');
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException('not implemented');
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/globals/defines.php
Normal file
48
src/globals/defines.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
// 工作目录
|
||||
use ZM\Logger\ConsoleLogger;
|
||||
|
||||
define('WORKING_DIR', getcwd());
|
||||
const ROOT_DIR = __DIR__ . '/../..';
|
||||
|
||||
// 程序启动时间
|
||||
define('START_TIME', microtime(true));
|
||||
|
||||
// 规定目录
|
||||
define('BUILD_ROOT_PATH', is_string($a = getenv('BUILD_ROOT_PATH')) ? $a : (WORKING_DIR . '/buildroot'));
|
||||
define('SOURCE_PATH', is_string($a = getenv('SOURCE_PATH')) ? $a : (WORKING_DIR . '/source'));
|
||||
define('DOWNLOAD_PATH', is_string($a = getenv('DOWNLOAD_PATH')) ? $a : (WORKING_DIR . '/downloads'));
|
||||
define('BUILD_LIB_PATH', is_string($a = getenv('INSTALL_LIB_PATH')) ? $a : (BUILD_ROOT_PATH . '/lib'));
|
||||
const BUILD_DEPS_PATH = BUILD_ROOT_PATH;
|
||||
define('BUILD_INCLUDE_PATH', is_string($a = getenv('INSTALL_INCLUDE_PATH')) ? $a : (BUILD_ROOT_PATH . '/include'));
|
||||
define('SEPARATED_PATH', [
|
||||
'/' . pathinfo(BUILD_LIB_PATH)['basename'], // lib
|
||||
'/' . pathinfo(BUILD_INCLUDE_PATH)['basename'], // include
|
||||
BUILD_ROOT_PATH,
|
||||
]);
|
||||
|
||||
// 危险的命令额外用 notice 级别提醒
|
||||
const DANGER_CMD = [
|
||||
'rm',
|
||||
'rmdir',
|
||||
];
|
||||
|
||||
// 替换方案
|
||||
const REPLACE_FILE_STR = 1;
|
||||
const REPLACE_FILE_PREG = 2;
|
||||
const REPLACE_FILE_USER = 3;
|
||||
|
||||
// 编译输出类型
|
||||
const BUILD_MICRO_NONE = 0;
|
||||
const BUILD_MICRO_ONLY = 1;
|
||||
const BUILD_MICRO_BOTH = 2;
|
||||
|
||||
// 编译状态
|
||||
const BUILD_STATUS_OK = 0;
|
||||
const BUILD_STATUS_ALREADY = 1;
|
||||
const BUILD_STATUS_FAILED = 2;
|
||||
|
||||
ConsoleLogger::$date_format = 'H:i:s';
|
||||
97
src/globals/functions.php
Normal file
97
src/globals/functions.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use ZM\Logger\ConsoleLogger;
|
||||
|
||||
/**
|
||||
* 判断传入的数组是否为关联数组
|
||||
* @param mixed $array
|
||||
*/
|
||||
function is_assoc_array($array): bool
|
||||
{
|
||||
return is_array($array) && (!empty($array) && array_keys($array) !== range(0, count($array) - 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 助手方法,返回一个 Logger 实例
|
||||
*/
|
||||
function logger(): LoggerInterface
|
||||
{
|
||||
global $ob_logger;
|
||||
if ($ob_logger === null) {
|
||||
return new ConsoleLogger();
|
||||
}
|
||||
return $ob_logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \SPC\exception\RuntimeException
|
||||
*/
|
||||
function arch2gnu(string $arch): string
|
||||
{
|
||||
$arch = strtolower($arch);
|
||||
return match ($arch) {
|
||||
'x86_64', 'x64', 'amd64' => 'x86_64',
|
||||
'arm64', 'aarch64' => 'aarch64',
|
||||
default => throw new \SPC\exception\RuntimeException('Not support arch: ' . $arch),
|
||||
// 'armv7' => 'arm',
|
||||
};
|
||||
}
|
||||
|
||||
function quote(string $str, string $quote = '"'): string
|
||||
{
|
||||
return $quote . $str . $quote;
|
||||
}
|
||||
|
||||
function osfamily2dir(): string
|
||||
{
|
||||
return match (PHP_OS_FAMILY) {
|
||||
/* @phpstan-ignore-next-line */
|
||||
'Windows', 'WINNT', 'Cygwin' => 'windows',
|
||||
'Darwin' => 'macos',
|
||||
'Linux' => 'linux',
|
||||
default => throw new \SPC\exception\RuntimeException('Not support os: ' . PHP_OS_FAMILY),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \SPC\exception\RuntimeException
|
||||
*/
|
||||
function f_passthru(string $cmd): ?bool
|
||||
{
|
||||
$danger = false;
|
||||
foreach (DANGER_CMD as $danger_cmd) {
|
||||
if (str_starts_with($cmd, $danger_cmd . ' ')) {
|
||||
$danger = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($danger) {
|
||||
logger()->notice('Running dangerous command: ' . $cmd);
|
||||
} else {
|
||||
logger()->debug('Running command with direct output: ' . $cmd);
|
||||
}
|
||||
$ret = passthru($cmd, $code);
|
||||
if ($code !== 0) {
|
||||
throw new \SPC\exception\RuntimeException('Command run failed with code[' . $code . ']: ' . $cmd, $code);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function f_exec(string $command, &$output, &$result_code)
|
||||
{
|
||||
logger()->debug('Running command (no output) : ' . $command);
|
||||
return exec($command, $output, $result_code);
|
||||
}
|
||||
|
||||
function f_mkdir(string $directory, int $permissions = 0777, bool $recursive = false): bool
|
||||
{
|
||||
if (file_exists($directory)) {
|
||||
logger()->debug("Dir {$directory} already exists, ignored");
|
||||
return true;
|
||||
}
|
||||
logger()->debug('Making new directory ' . ($recursive ? 'recursive' : '') . ': ' . $directory);
|
||||
return mkdir($directory, $permissions, $recursive);
|
||||
}
|
||||
8
src/globals/tests/bcmath.php
Normal file
8
src/globals/tests/bcmath.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
bcscale(3);
|
||||
exit(bcdiv('105', '6.55957') === '16.007' ? 0 : 1);
|
||||
7
src/globals/tests/calendar.php
Normal file
7
src/globals/tests/calendar.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpComposerExtensionStubsInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
exit(function_exists('cal_info') && is_array(cal_info(0)) ? 0 : 1);
|
||||
5
src/globals/tests/curl.php
Normal file
5
src/globals/tests/curl.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
exit(function_exists('curl_init') ? 0 : 1);
|
||||
5
src/globals/tests/dom.php
Normal file
5
src/globals/tests/dom.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
exit(class_exists('\\DOMDocument') ? 0 : 1);
|
||||
5
src/globals/tests/redis.php
Normal file
5
src/globals/tests/redis.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
exit(class_exists('\\Redis') ? 0 : 1);
|
||||
@@ -1,245 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
function downloadIt() {
|
||||
downloader="wget"
|
||||
type wget >/dev/null 2>&1 || { downloader="curl"; }
|
||||
if [ "$downloader" = "wget" ]; then
|
||||
_down_prefix="O"
|
||||
else
|
||||
_down_prefix="o"
|
||||
fi
|
||||
_down_symbol=0
|
||||
if [ ! -f "$2" ]; then
|
||||
$downloader "$1" -$_down_prefix "$2" >/dev/null 2>&1 && \
|
||||
echo "$1 完成!" && _down_symbol=1
|
||||
else
|
||||
echo "$1 已存在!" && _down_symbol=1
|
||||
fi
|
||||
if [ $_down_symbol == 0 ]; then
|
||||
echo "失败!请检查网络连接!"
|
||||
rm -rf "$2"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
_script_ver="1.1.1"
|
||||
|
||||
_php_ver="7.4.16"
|
||||
_swoole_ver="4.6.6"
|
||||
_redis_ver="5.3.4"
|
||||
_libxml2_ver="2.9.10"
|
||||
_curl_ver="7.76.1"
|
||||
|
||||
# 默认编译在脚本当前目录
|
||||
_home_dir=$(pwd)"/"
|
||||
|
||||
# PHP使用国内的搜狐镜像
|
||||
_php_down_link="http://mirrors.sohu.com/php/php-$_php_ver.tar.gz"
|
||||
# swoole使用自建的炸毛服务器的分发,因为pecl有时候在国内奇慢
|
||||
_swoole_down_link="https://dl.zhamao.me/swoole/swoole-$_swoole_ver.tgz"
|
||||
_swoole_down_link_bak="https://pecl.php.net/get/swoole-$_swoole_ver.tgz"
|
||||
# phpredis也使用自建的炸毛服务器分发
|
||||
_redis_down_link="https://dl.zhamao.me/phpredis/redis-$_redis_ver.tgz"
|
||||
_redis_down_link_bak="https://pecl.php.net/get/redis-$_redis_ver.tgz"
|
||||
# libxml2也使用自建的服务器
|
||||
_libxml2_down_link="https://dl.zhamao.me/libxml2/libxml2-$_libxml2_ver.tar.gz"
|
||||
_libxml2_down_link_bak="http://xmlsoft.org/sources/libxml2-$_libxml2_ver.tar.gz"
|
||||
# liblzma是自建的服务器,如果需要找原始位置,在GitHub搜索liblzma即可
|
||||
_liblzma_down_link="https://dl.zhamao.me/liblzma/liblzma.tar.gz"
|
||||
# curl/libcurl使用自建的服务器,bak是源地址
|
||||
_curl_down_link="https://dl.zhamao.me/curl/curl-$_curl_ver.tar.gz"
|
||||
_curl_down_link_bak="https://curl.haxx.se/download/curl-$_curl_ver.tar.gz"
|
||||
|
||||
_curl_override_1='
|
||||
AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
$3
|
||||
])
|
||||
'
|
||||
|
||||
_curl_override_2='
|
||||
AC_DEFUN([PHP_CHECK_LIBRARY], [
|
||||
save_old_LDFLAGS=$LDFLAGS
|
||||
ac_stuff="$5"
|
||||
|
||||
save_ext_shared=$ext_shared
|
||||
ext_shared=yes
|
||||
PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS)
|
||||
AC_CHECK_LIB([$1],[$2],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
$3
|
||||
],[
|
||||
LDFLAGS=$save_old_LDFLAGS
|
||||
ext_shared=$save_ext_shared
|
||||
unset ac_cv_lib_$1[]_$2
|
||||
$4
|
||||
])dnl
|
||||
])
|
||||
'
|
||||
|
||||
function downloadAll() {
|
||||
mkdir "$_home_dir""source" > /dev/null 2>&1
|
||||
|
||||
echo "正在下载 php 源码 "$_php_down_link" ..."
|
||||
downloadIt $_php_down_link "$_home_dir""source/php.tar.gz" || { exit 1; } &
|
||||
|
||||
#echo "正在下载 openssl 源码 "$_openssl_down_link" ..."
|
||||
#downloadIt $_openssl_down_link "$_home_dir""source/openssl.tar.gz" || { exit; } &
|
||||
|
||||
echo "正在下载 swoole 源码 "$_swoole_down_link" ..."
|
||||
downloadIt $_swoole_down_link "$_home_dir""source/swoole.tar.gz" || { exit 1; } &
|
||||
|
||||
echo "正在下载 redis 源码 "$_redis_down_link" ..."
|
||||
downloadIt $_redis_down_link "$_home_dir""source/redis.tar.gz" || { exit 1; } &
|
||||
|
||||
echo "正在下载 libxml2 源码 ..."
|
||||
downloadIt $_libxml2_down_link "$_home_dir""source/libxml2.tar.gz" || { exit 1; } &
|
||||
|
||||
echo "正在下载 liblzma 源码 ..."
|
||||
downloadIt $_liblzma_down_link "$_home_dir""source/liblzma.tar.gz" || { exit 1; } &
|
||||
|
||||
echo "正在下载 curl 源码 ..."
|
||||
downloadIt $_curl_down_link "$_home_dir""source/curl.tar.gz" || { exit 1; } &
|
||||
|
||||
wait
|
||||
}
|
||||
|
||||
function compileLiblzma() {
|
||||
if [ -f "$_home_dir""opt/liblzma/lib/liblzma.so" ]; then
|
||||
echo "已编译 liblzma!" && return
|
||||
fi
|
||||
tar -xf "$_home_dir""source/liblzma.tar.gz" -C "$_home_dir""source/" && \
|
||||
cd "$_home_dir""source/liblzma" && \
|
||||
./configure --prefix="$_home_dir""opt/liblzma" && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "编译 liblzma 完成!"
|
||||
}
|
||||
|
||||
function compileCurl() {
|
||||
if [ -f "$_home_dir""opt/curl/bin/curl" ]; then
|
||||
echo "已编译 curl!" && return
|
||||
fi
|
||||
tar -xf "$_home_dir""source/curl.tar.gz" -C "$_home_dir""source/" && \
|
||||
cd "$_home_dir""source/curl-""$_curl_ver" && \
|
||||
CC=gcc CXX=g++ CFLAGS=-fPIC CPPFLAGS=-fPIC ./configure --prefix="$_home_dir""opt/curl" \
|
||||
--without-nghttp2 \
|
||||
--with-ssl=/usr \
|
||||
--with-pic=pic \
|
||||
--enable-ipv6 \
|
||||
--enable-shared=no \
|
||||
--without-libidn2 \
|
||||
--disable-ldap \
|
||||
--without-libpsl \
|
||||
--without-lber \
|
||||
--enable-ares && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "编译 curl 完成!"
|
||||
}
|
||||
|
||||
function compileLibxml2() {
|
||||
if [ -f "$_home_dir""opt/libxml2/lib/libxml2.so" ]; then
|
||||
echo "已编译 libxml2!" && return
|
||||
fi
|
||||
tar -xf "$_home_dir""source/libxml2.tar.gz" -C "$_home_dir""source/" && \
|
||||
cd "$_home_dir""source/libxml2-""$_libxml2_ver" && \
|
||||
./configure --with-lzma="$_home_dir""opt/liblzma" --prefix="$_home_dir""opt/libxml2" --exec-prefix="$_home_dir""opt/libxml2" --without-python && \
|
||||
make -j4 && \
|
||||
make install && \
|
||||
echo "编译 libxml2 完成!"
|
||||
}
|
||||
|
||||
function compilePHPWithSwoole() {
|
||||
echo "正在编译 php ..." && \
|
||||
rm -rf "$_home_dir""source/php-""$_php_ver" && \
|
||||
tar -xf "$_home_dir""source/php.tar.gz" -C "$_home_dir""source/" && \
|
||||
#git clone --depth=1 https://fgit.zhamao.me/swoole/swoole-src.git "$_home_dir""source/"swoole-$_swoole_ver && \
|
||||
tar -xf "$_home_dir""source/swoole.tar.gz" -C "$_home_dir""source/" && \
|
||||
mv "$_home_dir""source/swoole-""$_swoole_ver" "$_home_dir""source/php-""$_php_ver/ext/swoole" && \
|
||||
"$_home_dir""source/php-""$_php_ver/ext/swoole/clear.sh" && \
|
||||
tar -xf "$_home_dir""source/redis.tar.gz" -C "$_home_dir""source/" && \
|
||||
mv "$_home_dir""source/redis-""$_redis_ver" "$_home_dir""source/php-""$_php_ver/ext/redis" && \
|
||||
cd "$_home_dir""source/php-""$_php_ver/" && \
|
||||
echo "$_curl_override_1" > "$_home_dir""ac_override_1" && \
|
||||
echo "$_curl_override_2" > "$_home_dir""ac_override_2" && \
|
||||
cat "$_home_dir""ac_override_1" "$_home_dir""source/php-""$_php_ver/ext/curl/config.m4" "$_home_dir""ac_override_2" > /tmp/aa && \
|
||||
mv /tmp/aa "$_home_dir""source/php-""$_php_ver/ext/curl/config.m4" && \
|
||||
./buildconf --force && \
|
||||
PKG_CONFIG_PATH="$PKG_CONFIG_PATH:""$_home_dir""opt/libxml2/lib/pkgconfig" && \
|
||||
PKG_CONFIG_PATH="$PKG_CONFIG_PATH:""$_home_dir""opt/curl/lib/pkgconfig" ./configure LDFLAGS=-static \
|
||||
--prefix="$_home_dir""php-dist" \
|
||||
--disable-all \
|
||||
--enable-shared=no \
|
||||
--enable-static=yes \
|
||||
--enable-inline-optimization \
|
||||
--with-layout=GNU \
|
||||
--enable-calendar \
|
||||
--enable-ctype \
|
||||
--enable-filter \
|
||||
--enable-openssl \
|
||||
--enable-bcmath \
|
||||
--with-openssl-dir="/usr" \
|
||||
--enable-pcntl \
|
||||
--enable-openssl \
|
||||
--with-openssl \
|
||||
--with-iconv \
|
||||
--enable-json \
|
||||
--enable-mbstring \
|
||||
--enable-phar \
|
||||
--enable-pdo \
|
||||
--with-pdo-mysql=mysqlnd \
|
||||
--enable-sockets \
|
||||
--enable-swoole \
|
||||
--enable-gd \
|
||||
--enable-redis \
|
||||
--disable-redis-session \
|
||||
--enable-simplexml \
|
||||
--enable-dom \
|
||||
--with-libxml="$_home_dir""opt/libxml2" \
|
||||
--enable-xml \
|
||||
--enable-xmlwriter \
|
||||
--enable-xmlreader \
|
||||
--with-zlib \
|
||||
--enable-posix \
|
||||
--enable-mysqlnd \
|
||||
--enable-tokenizer \
|
||||
--with-curl="$_home_dir""opt/curl" \
|
||||
--with-pear=no \
|
||||
--disable-pear \
|
||||
--disable-cgi \
|
||||
--disable-phpdbg && \
|
||||
sed -ie 's/-export-dynamic//g' "$_home_dir""source/php-""$_php_ver/Makefile" && \
|
||||
sed -ie 's/-o $(SAPI_CLI_PATH)/-all-static -o $(SAPI_CLI_PATH)/g' "$_home_dir""source/php-""$_php_ver/Makefile" && \
|
||||
sed -ie 's/swoole_clock_gettime(CLOCK_REALTIME/clock_gettime(CLOCK_REALTIME/g' "$_home_dir""source/php-""$_php_ver/ext/swoole/include/swoole.h" && \
|
||||
make LDFLAGS=-ldl -j4 && \
|
||||
make install && \
|
||||
strip "$_home_dir""php-dist/bin/php"
|
||||
}
|
||||
|
||||
#apk add g++ pkgconf autoconf nghttp2 libcurl \
|
||||
# git gcc zlib-dev libstdc++ ncurses zlib linux-headers \
|
||||
# readline make libssl1.1 libxml2 m4 libgcc vim binutils \
|
||||
# oniguruma-dev openssl openssl-dev
|
||||
# 编译必需的
|
||||
apk add gcc g++ autoconf libstdc++ linux-headers make m4 libgcc binutils ncurses
|
||||
# php的zlib支持
|
||||
apk add zlib-dev zlib-static
|
||||
# php的mbstring支持
|
||||
apk add oniguruma-dev
|
||||
# php的openssl支持
|
||||
apk add openssl-libs-static openssl-dev openssl
|
||||
# php的gd支持,如果不需要gd则去掉--enable-gd和下面的依赖
|
||||
apk add libpng-dev libpng-static
|
||||
# curl的c-ares支持,如果不需要curl则去掉
|
||||
apk add c-ares-static c-ares-dev
|
||||
|
||||
|
||||
downloadAll && \
|
||||
compileLiblzma && \
|
||||
compileLibxml2 && \
|
||||
compileCurl && \
|
||||
compilePHPWithSwoole && \
|
||||
echo "完成!见 php-dist/bin/php"
|
||||
|
||||
Reference in New Issue
Block a user