mirror of https://gitlab.com/ita1024/waf.git
Api docs
This commit is contained in:
parent
21e9be8cdb
commit
ccc77cd9f5
|
@ -3,7 +3,7 @@
|
||||||
# Thomas Nagy, 2010-2016 (ita)
|
# Thomas Nagy, 2010-2016 (ita)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Classes and functions required for waf commands
|
Classes and functions enabling the command system
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, re, imp, sys
|
import os, re, imp, sys
|
||||||
|
@ -54,7 +54,7 @@ waf_dir = ''
|
||||||
|
|
||||||
g_module = None
|
g_module = None
|
||||||
"""
|
"""
|
||||||
Module representing the main wscript file (see :py:const:`waflib.Context.run_dir`)
|
Module representing the top-level wscript file (see :py:const:`waflib.Context.run_dir`)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
STDOUT = 1
|
STDOUT = 1
|
||||||
|
@ -69,15 +69,17 @@ are added automatically by a metaclass.
|
||||||
|
|
||||||
def create_context(cmd_name, *k, **kw):
|
def create_context(cmd_name, *k, **kw):
|
||||||
"""
|
"""
|
||||||
Create a new :py:class:`waflib.Context.Context` instance corresponding to the given command.
|
Returns a new :py:class:`waflib.Context.Context` instance corresponding to the given command.
|
||||||
Used in particular by :py:func:`waflib.Scripting.run_command`
|
Used in particular by :py:func:`waflib.Scripting.run_command`
|
||||||
|
|
||||||
:param cmd_name: command
|
:param cmd_name: command name
|
||||||
:type cmd_name: string
|
:type cmd_name: string
|
||||||
:param k: arguments to give to the context class initializer
|
:param k: arguments to give to the context class initializer
|
||||||
:type k: list
|
:type k: list
|
||||||
:param k: keyword arguments to give to the context class initializer
|
:param k: keyword arguments to give to the context class initializer
|
||||||
:type k: dict
|
:type k: dict
|
||||||
|
:return: Context object
|
||||||
|
:rtype: :py:class:`waflib.Context.Context`
|
||||||
"""
|
"""
|
||||||
global classes
|
global classes
|
||||||
for x in classes:
|
for x in classes:
|
||||||
|
@ -89,8 +91,9 @@ def create_context(cmd_name, *k, **kw):
|
||||||
|
|
||||||
class store_context(type):
|
class store_context(type):
|
||||||
"""
|
"""
|
||||||
Metaclass for storing the command classes into the list :py:const:`waflib.Context.classes`
|
Metaclass that registers command classes into the list :py:const:`waflib.Context.classes`
|
||||||
Context classes must provide an attribute 'cmd' representing the command to execute
|
Context classes must provide an attribute 'cmd' representing the command name, and a function
|
||||||
|
attribute 'fun' representing the function name that the command uses.
|
||||||
"""
|
"""
|
||||||
def __init__(cls, name, bases, dict):
|
def __init__(cls, name, bases, dict):
|
||||||
super(store_context, cls).__init__(name, bases, dict)
|
super(store_context, cls).__init__(name, bases, dict)
|
||||||
|
@ -111,7 +114,7 @@ class store_context(type):
|
||||||
classes.insert(0, cls)
|
classes.insert(0, cls)
|
||||||
|
|
||||||
ctx = store_context('ctx', (object,), {})
|
ctx = store_context('ctx', (object,), {})
|
||||||
"""Base class for the :py:class:`waflib.Context.Context` classes"""
|
"""Base class for all :py:class:`waflib.Context.Context` classes"""
|
||||||
|
|
||||||
class Context(ctx):
|
class Context(ctx):
|
||||||
"""
|
"""
|
||||||
|
@ -122,7 +125,7 @@ class Context(ctx):
|
||||||
def foo(ctx):
|
def foo(ctx):
|
||||||
print(ctx.__class__.__name__) # waflib.Context.Context
|
print(ctx.__class__.__name__) # waflib.Context.Context
|
||||||
|
|
||||||
Subclasses must define the attribute 'cmd':
|
Subclasses must define the class attributes 'cmd' and 'fun':
|
||||||
|
|
||||||
:param cmd: command to execute as in ``waf cmd``
|
:param cmd: command to execute as in ``waf cmd``
|
||||||
:type cmd: string
|
:type cmd: string
|
||||||
|
@ -140,7 +143,7 @@ class Context(ctx):
|
||||||
|
|
||||||
tools = {}
|
tools = {}
|
||||||
"""
|
"""
|
||||||
A cache for modules (wscript files) read by :py:meth:`Context.Context.load`
|
A module cache for wscript files; see :py:meth:`Context.Context.load`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, **kw):
|
def __init__(self, **kw):
|
||||||
|
@ -152,7 +155,7 @@ class Context(ctx):
|
||||||
|
|
||||||
# binds the context to the nodes in use to avoid a context singleton
|
# binds the context to the nodes in use to avoid a context singleton
|
||||||
self.node_class = type('Nod3', (waflib.Node.Node,), {})
|
self.node_class = type('Nod3', (waflib.Node.Node,), {})
|
||||||
self.node_class.__module__ = "waflib.Node"
|
self.node_class.__module__ = 'waflib.Node'
|
||||||
self.node_class.ctx = self
|
self.node_class.ctx = self
|
||||||
|
|
||||||
self.root = self.node_class('', None)
|
self.root = self.node_class('', None)
|
||||||
|
@ -165,7 +168,7 @@ class Context(ctx):
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
"""
|
"""
|
||||||
Use to free resources such as open files potentially held by the logger
|
Called to free resources such as logger files
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
logger = self.logger
|
logger = self.logger
|
||||||
|
@ -177,11 +180,11 @@ class Context(ctx):
|
||||||
|
|
||||||
def load(self, tool_list, *k, **kw):
|
def load(self, tool_list, *k, **kw):
|
||||||
"""
|
"""
|
||||||
Load a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` from it.
|
Loads a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun`
|
||||||
A ``tooldir`` value may be provided as a list of module paths.
|
from it. A ``tooldir`` argument may be provided as a list of module paths.
|
||||||
|
|
||||||
|
:param tool_list: list of Waf tool names to load
|
||||||
:type tool_list: list of string or space-separated string
|
:type tool_list: list of string or space-separated string
|
||||||
:param tool_list: list of Waf tools to use
|
|
||||||
"""
|
"""
|
||||||
tools = Utils.to_list(tool_list)
|
tools = Utils.to_list(tool_list)
|
||||||
path = Utils.to_list(kw.get('tooldir', ''))
|
path = Utils.to_list(kw.get('tooldir', ''))
|
||||||
|
@ -195,15 +198,17 @@ class Context(ctx):
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
"""
|
"""
|
||||||
Execute the command. Redefine this method in subclasses.
|
Here, it calls the function name in the top-level wscript file. Most subclasses
|
||||||
|
redefine this method to provide additional functionality.
|
||||||
"""
|
"""
|
||||||
global g_module
|
global g_module
|
||||||
self.recurse([os.path.dirname(g_module.root_path)])
|
self.recurse([os.path.dirname(g_module.root_path)])
|
||||||
|
|
||||||
def pre_recurse(self, node):
|
def pre_recurse(self, node):
|
||||||
"""
|
"""
|
||||||
Method executed immediately before a folder is read by :py:meth:`waflib.Context.Context.recurse`. The node given is set
|
Method executed immediately before a folder is read by :py:meth:`waflib.Context.Context.recurse`.
|
||||||
as an attribute ``self.cur_script``, and as the current path ``self.path``
|
The current script is bound as a Node object on ``self.cur_script``, and the current path
|
||||||
|
is bound to ``self.path``
|
||||||
|
|
||||||
:param node: script
|
:param node: script
|
||||||
:type node: :py:class:`waflib.Node.Node`
|
:type node: :py:class:`waflib.Node.Node`
|
||||||
|
@ -215,7 +220,7 @@ class Context(ctx):
|
||||||
|
|
||||||
def post_recurse(self, node):
|
def post_recurse(self, node):
|
||||||
"""
|
"""
|
||||||
Restore ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates.
|
Restores ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates.
|
||||||
|
|
||||||
:param node: script
|
:param node: script
|
||||||
:type node: :py:class:`waflib.Node.Node`
|
:type node: :py:class:`waflib.Node.Node`
|
||||||
|
@ -226,10 +231,13 @@ class Context(ctx):
|
||||||
|
|
||||||
def recurse(self, dirs, name=None, mandatory=True, once=True, encoding=None):
|
def recurse(self, dirs, name=None, mandatory=True, once=True, encoding=None):
|
||||||
"""
|
"""
|
||||||
Run user code from the supplied list of directories.
|
Runs user-provided functions from the supplied list of directories.
|
||||||
The directories can be either absolute, or relative to the directory
|
The directories can be either absolute, or relative to the directory
|
||||||
of the wscript file. The methods :py:meth:`waflib.Context.Context.pre_recurse` and :py:meth:`waflib.Context.Context.post_recurse`
|
of the wscript file
|
||||||
are called immediately before and after a script has been executed.
|
|
||||||
|
The methods :py:meth:`waflib.Context.Context.pre_recurse` and
|
||||||
|
:py:meth:`waflib.Context.Context.post_recurse` are called immediately before
|
||||||
|
and after a script has been executed.
|
||||||
|
|
||||||
:param dirs: List of directories to visit
|
:param dirs: List of directories to visit
|
||||||
:type dirs: list of string or space-separated string
|
:type dirs: list of string or space-separated string
|
||||||
|
@ -290,18 +298,22 @@ class Context(ctx):
|
||||||
|
|
||||||
def exec_command(self, cmd, **kw):
|
def exec_command(self, cmd, **kw):
|
||||||
"""
|
"""
|
||||||
Execute a command and return the exit status. If the context has the attribute 'log',
|
Runs an external process and returns the exit status::
|
||||||
capture and log the process stderr/stdout for logging purposes::
|
|
||||||
|
|
||||||
def run(tsk):
|
def run(tsk):
|
||||||
ret = tsk.generator.bld.exec_command('touch foo.txt')
|
ret = tsk.generator.bld.exec_command('touch foo.txt')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
This method captures the standard/error outputs (Issue 1101), but it does not return the values
|
If the context has the attribute 'log', then captures and logs the process stderr/stdout.
|
||||||
unlike :py:meth:`waflib.Context.Context.cmd_and_log`
|
Unlike :py:meth:`waflib.Context.Context.cmd_and_log`, this method does not return the
|
||||||
|
stdout/stderr values captured.
|
||||||
|
|
||||||
:param cmd: command argument for subprocess.Popen
|
:param cmd: command argument for subprocess.Popen
|
||||||
|
:type cmd: string or list
|
||||||
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
|
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
|
||||||
|
:type kw: dict
|
||||||
|
:returns: process exit status
|
||||||
|
:rtype: integer
|
||||||
"""
|
"""
|
||||||
subprocess = Utils.subprocess
|
subprocess = Utils.subprocess
|
||||||
kw['shell'] = isinstance(cmd, str)
|
kw['shell'] = isinstance(cmd, str)
|
||||||
|
@ -358,7 +370,7 @@ class Context(ctx):
|
||||||
|
|
||||||
def cmd_and_log(self, cmd, **kw):
|
def cmd_and_log(self, cmd, **kw):
|
||||||
"""
|
"""
|
||||||
Execute a command and return stdout/stderr if the execution is successful.
|
Executes a proces and returns stdout/stderr if the execution is successful.
|
||||||
An exception is thrown when the exit status is non-0. In that case, both stderr and stdout
|
An exception is thrown when the exit status is non-0. In that case, both stderr and stdout
|
||||||
will be bound to the WafError object::
|
will be bound to the WafError object::
|
||||||
|
|
||||||
|
@ -372,7 +384,13 @@ class Context(ctx):
|
||||||
print(e.stdout, e.stderr)
|
print(e.stdout, e.stderr)
|
||||||
|
|
||||||
:param cmd: args for subprocess.Popen
|
:param cmd: args for subprocess.Popen
|
||||||
|
:type cmd: list or string
|
||||||
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
|
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
|
||||||
|
:type kw: dict
|
||||||
|
:returns: process exit status
|
||||||
|
:rtype: integer
|
||||||
|
:raises: :py:class:`waflib.Errors.WafError` if an invalid executable is specified for a non-shell process
|
||||||
|
:raises: :py:class:`waflib.Errors.WafError` in case of execution failure; stdout/stderr/returncode are bound to the exception object
|
||||||
"""
|
"""
|
||||||
subprocess = Utils.subprocess
|
subprocess = Utils.subprocess
|
||||||
kw['shell'] = isinstance(cmd, str)
|
kw['shell'] = isinstance(cmd, str)
|
||||||
|
@ -442,7 +460,8 @@ class Context(ctx):
|
||||||
|
|
||||||
def fatal(self, msg, ex=None):
|
def fatal(self, msg, ex=None):
|
||||||
"""
|
"""
|
||||||
Raise a configuration error to interrupt the execution immediately::
|
Prints an error message in red and stops command execution; this is
|
||||||
|
usually used in the configuration section::
|
||||||
|
|
||||||
def configure(conf):
|
def configure(conf):
|
||||||
conf.fatal('a requirement is missing')
|
conf.fatal('a requirement is missing')
|
||||||
|
@ -451,6 +470,7 @@ class Context(ctx):
|
||||||
:type msg: string
|
:type msg: string
|
||||||
:param ex: optional exception object
|
:param ex: optional exception object
|
||||||
:type ex: exception
|
:type ex: exception
|
||||||
|
:raises: :py:class:`waflib.Errors.ConfigurationError`
|
||||||
"""
|
"""
|
||||||
if self.logger:
|
if self.logger:
|
||||||
self.logger.info('from %s: %s' % (self.path.abspath(), msg))
|
self.logger.info('from %s: %s' % (self.path.abspath(), msg))
|
||||||
|
@ -462,13 +482,13 @@ class Context(ctx):
|
||||||
|
|
||||||
def to_log(self, msg):
|
def to_log(self, msg):
|
||||||
"""
|
"""
|
||||||
Log some information to the logger (if present), or to stderr. If the message is empty,
|
Logs information to the logger (if present), or to stderr.
|
||||||
it is not printed::
|
Empty messages are not printed::
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
bld.to_log('starting the build')
|
bld.to_log('starting the build')
|
||||||
|
|
||||||
When in doubt, override this method, or provide a logger on the context class.
|
Provide a logger on the context class or override this methid if necessary.
|
||||||
|
|
||||||
:param msg: message
|
:param msg: message
|
||||||
:type msg: string
|
:type msg: string
|
||||||
|
@ -484,7 +504,7 @@ class Context(ctx):
|
||||||
|
|
||||||
def msg(self, *k, **kw):
|
def msg(self, *k, **kw):
|
||||||
"""
|
"""
|
||||||
Print a configuration message of the form ``msg: result``.
|
Prints a configuration message of the form ``msg: result``.
|
||||||
The second part of the message will be in colors. The output
|
The second part of the message will be in colors. The output
|
||||||
can be disabled easly by setting ``in_msg`` to a positive value::
|
can be disabled easly by setting ``in_msg`` to a positive value::
|
||||||
|
|
||||||
|
@ -520,7 +540,7 @@ class Context(ctx):
|
||||||
|
|
||||||
def start_msg(self, *k, **kw):
|
def start_msg(self, *k, **kw):
|
||||||
"""
|
"""
|
||||||
Print the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg`
|
Prints the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg`
|
||||||
"""
|
"""
|
||||||
if kw.get('quiet'):
|
if kw.get('quiet'):
|
||||||
return
|
return
|
||||||
|
@ -543,7 +563,7 @@ class Context(ctx):
|
||||||
Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='')
|
Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='')
|
||||||
|
|
||||||
def end_msg(self, *k, **kw):
|
def end_msg(self, *k, **kw):
|
||||||
"""Print the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`"""
|
"""Prints the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`"""
|
||||||
if kw.get('quiet'):
|
if kw.get('quiet'):
|
||||||
return
|
return
|
||||||
self.in_msg -= 1
|
self.in_msg -= 1
|
||||||
|
@ -573,6 +593,17 @@ class Context(ctx):
|
||||||
Logs.pprint(color, msg)
|
Logs.pprint(color, msg)
|
||||||
|
|
||||||
def load_special_tools(self, var, ban=[]):
|
def load_special_tools(self, var, ban=[]):
|
||||||
|
"""
|
||||||
|
Loads third-party extensions modules for certain programming languages
|
||||||
|
by trying to list certain files in the extras/ directory. This method
|
||||||
|
is typically called once for a programming language group, see for
|
||||||
|
example :py:mod:`waflib.Tools.compiler_c`
|
||||||
|
|
||||||
|
:param var: glob expression, for example 'cxx\_\*.py'
|
||||||
|
:type var: string
|
||||||
|
:param ban: list of exact file names to exclude
|
||||||
|
:type ban: list of string
|
||||||
|
"""
|
||||||
global waf_dir
|
global waf_dir
|
||||||
if os.path.isdir(waf_dir):
|
if os.path.isdir(waf_dir):
|
||||||
lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var)
|
lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var)
|
||||||
|
@ -598,13 +629,13 @@ class Context(ctx):
|
||||||
|
|
||||||
cache_modules = {}
|
cache_modules = {}
|
||||||
"""
|
"""
|
||||||
Dictionary holding already loaded modules, keyed by their absolute path.
|
Dictionary holding already loaded modules (wscript), indexed by their absolute path.
|
||||||
The modules are added automatically by :py:func:`waflib.Context.load_module`
|
The modules are added automatically by :py:func:`waflib.Context.load_module`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def load_module(path, encoding=None):
|
def load_module(path, encoding=None):
|
||||||
"""
|
"""
|
||||||
Load a source file as a python module.
|
Loads a wscript file as a python module. This method caches results in :py:attr:`waflib.Context.cache_modules`
|
||||||
|
|
||||||
:param path: file path
|
:param path: file path
|
||||||
:type path: string
|
:type path: string
|
||||||
|
@ -624,17 +655,17 @@ def load_module(path, encoding=None):
|
||||||
|
|
||||||
module_dir = os.path.dirname(path)
|
module_dir = os.path.dirname(path)
|
||||||
sys.path.insert(0, module_dir)
|
sys.path.insert(0, module_dir)
|
||||||
|
try:
|
||||||
try : exec(compile(code, path, 'exec'), module.__dict__)
|
exec(compile(code, path, 'exec'), module.__dict__)
|
||||||
finally: sys.path.remove(module_dir)
|
finally:
|
||||||
|
sys.path.remove(module_dir)
|
||||||
|
|
||||||
cache_modules[path] = module
|
cache_modules[path] = module
|
||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
|
def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
|
||||||
"""
|
"""
|
||||||
Import a Waf tool (python module), and store it in the dict :py:const:`waflib.Context.Context.tools`
|
Importx a Waf tool as a python module, and stores it in the dict :py:const:`waflib.Context.Context.tools`
|
||||||
|
|
||||||
:type tool: string
|
:type tool: string
|
||||||
:param tool: Name of the tool
|
:param tool: Name of the tool
|
||||||
|
@ -648,8 +679,9 @@ def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
|
||||||
else:
|
else:
|
||||||
tool = tool.replace('++', 'xx')
|
tool = tool.replace('++', 'xx')
|
||||||
|
|
||||||
origSysPath = sys.path
|
if not with_sys_path:
|
||||||
if not with_sys_path: sys.path = []
|
back_path = sys.path
|
||||||
|
sys.path = []
|
||||||
try:
|
try:
|
||||||
if tooldir:
|
if tooldir:
|
||||||
assert isinstance(tooldir, list)
|
assert isinstance(tooldir, list)
|
||||||
|
@ -679,5 +711,6 @@ def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
|
||||||
Context.tools[tool] = ret
|
Context.tools[tool] = ret
|
||||||
return ret
|
return ret
|
||||||
finally:
|
finally:
|
||||||
if not with_sys_path: sys.path += origSysPath
|
if not with_sys_path:
|
||||||
|
sys.path += back_path
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# Thomas Nagy, 2005-2010 (ita)
|
# Thomas Nagy, 2005-2016 (ita)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
logging, colors, terminal width and pretty-print
|
logging, colors, terminal width and pretty-print
|
||||||
|
@ -81,8 +81,9 @@ get_term_cols.__doc__ = """
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_color(cl):
|
def get_color(cl):
|
||||||
if not colors_lst['USE']: return ''
|
if colors_lst['USE']:
|
||||||
return colors_lst.get(cl, '')
|
return colors_lst.get(cl, '')
|
||||||
|
return ''
|
||||||
|
|
||||||
class color_dict(object):
|
class color_dict(object):
|
||||||
"""attribute-based color access, eg: colors.PINK"""
|
"""attribute-based color access, eg: colors.PINK"""
|
||||||
|
|
|
@ -2,23 +2,19 @@
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# Thomas Nagy, 2010-2015 (ita)
|
# Thomas Nagy, 2010-2015 (ita)
|
||||||
|
|
||||||
"""
|
|
||||||
burn a book, save a tree
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
all_modifs = {}
|
all_modifs = {}
|
||||||
|
|
||||||
def fixdir(dir):
|
def fixdir(dir):
|
||||||
"""call all the substitution functions on the waf folders"""
|
"""Call all substitution functions on Waf folders"""
|
||||||
global all_modifs
|
global all_modifs
|
||||||
for k in all_modifs:
|
for k in all_modifs:
|
||||||
for v in all_modifs[k]:
|
for v in all_modifs[k]:
|
||||||
modif(os.path.join(dir, 'waflib'), k, v)
|
modif(os.path.join(dir, 'waflib'), k, v)
|
||||||
|
|
||||||
def modif(dir, name, fun):
|
def modif(dir, name, fun):
|
||||||
"""execute a substitution function"""
|
"""Call a substitution function"""
|
||||||
if name == '*':
|
if name == '*':
|
||||||
lst = []
|
lst = []
|
||||||
for y in '. Tools extras'.split():
|
for y in '. Tools extras'.split():
|
||||||
|
|
Loading…
Reference in New Issue