3 Builder object subsystem.
5 A Builder object is a callable that encapsulates information about how
6 to execute actions to create a target Node (file) from source Nodes
7 (files), and how to create those dependencies for tracking.
9 The main entry point here is the Builder() factory method. This provides
10 a procedural interface that creates the right underlying Builder object
11 based on the keyword arguments supplied and the types of the arguments.
13 The goal is for this external interface to be simple enough that the
14 vast majority of users can create new Builders as necessary to support
15 building new types of files in their configurations, without having to
16 dive any deeper into this subsystem.
18 The base class here is BuilderBase. This is a concrete base class which
19 does, in fact, represent the Builder objects that we (or users) create.
21 There is also a proxy that looks like a Builder:
25 This proxies for a Builder with an action that is actually a
26 dictionary that knows how to map file suffixes to a specific
27 action. This is so that we can invoke different actions
28 (compilers, compile options) for different flavors of source
31 Builders and their proxies have the following public interface methods
32 used by other modules:
35 THE public interface. Calling a Builder object (with the
36 use of internal helper methods) sets up the target and source
37 dependencies, appropriate mapping to a specific action, and the
38 environment manipulation necessary for overridden construction
39 variable. This also takes care of warning about possible mistakes
43 Adds an emitter for a specific file suffix, used by some Tool
44 modules to specify that (for example) a yacc invocation on a .y
45 can create a .h *and* a .c file.
48 Adds an action for a specific file suffix, heavily used by
49 Tool modules to add their specific action(s) for turning
50 a source file into an object file to the global static
51 and shared object file Builders.
53 There are the following methods for internal use within this module:
56 The internal method that handles the heavily lifting when a
57 Builder is called. This is used so that the __call__() methods
58 can set up warning about possible mistakes in keyword-argument
59 overrides, and *then* execute all of the steps necessary so that
60 the warnings only occur once.
63 Returns the Builder's name within a specific Environment,
64 primarily used to try to return helpful information in error
72 Miscellaneous stuff for handling the prefix and suffix
73 manipulation we use in turning source file names into target
81 # Permission is hereby granted, free of charge, to any person obtaining
82 # a copy of this software and associated documentation files (the
83 # "Software"), to deal in the Software without restriction, including
84 # without limitation the rights to use, copy, modify, merge, publish,
85 # distribute, sublicense, and/or sell copies of the Software, and to
86 # permit persons to whom the Software is furnished to do so, subject to
87 # the following conditions:
89 # The above copyright notice and this permission notice shall be included
90 # in all copies or substantial portions of the Software.
92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
95 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
96 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
97 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
98 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
100 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
102 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
107 from SCons.Debug import logInstanceCreation
108 from SCons.Errors import InternalError, UserError
109 import SCons.Executor
114 import SCons.Warnings
121 def match_splitext(path, suffixes = []):
123 matchsuf = [S for S in suffixes if path[-len(S):] == S]
125 suf = max(list(map(None, list(map(len, matchsuf)), matchsuf)))[1]
126 return [path[:-len(suf)], path[-len(suf):]]
127 return SCons.Util.splitext(path)
129 class DictCmdGenerator(SCons.Util.Selector):
130 """This is a callable class that can be used as a
131 command generator function. It holds on to a dictionary
132 mapping file suffixes to Actions. It uses that dictionary
133 to return the proper action based on the file suffix of
136 def __init__(self, dict=None, source_ext_match=1):
137 SCons.Util.Selector.__init__(self, dict)
138 self.source_ext_match = source_ext_match
140 def src_suffixes(self):
143 def add_action(self, suffix, action):
144 """Add a suffix-action pair to the mapping.
146 self[suffix] = action
148 def __call__(self, target, source, env, for_signature):
152 if self.source_ext_match:
153 suffixes = self.src_suffixes()
155 for src in map(str, source):
156 my_ext = match_splitext(src, suffixes)[1]
157 if ext and my_ext != ext:
158 raise UserError("While building `%s' from `%s': Cannot build multiple sources with different extensions: %s, %s"
159 % (repr(list(map(str, target))), src, ext, my_ext))
162 ext = match_splitext(str(source[0]), self.src_suffixes())[1]
166 raise UserError("While building `%s': "
167 "Cannot deduce file extension from source files: %s"
168 % (repr(list(map(str, target))), repr(list(map(str, source)))))
171 ret = SCons.Util.Selector.__call__(self, env, source, ext)
173 raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (e[0], e[1], e[2]))
175 raise UserError("While building `%s' from `%s': Don't know how to build from a source file with suffix `%s'. Expected a suffix in this list: %s." % \
176 (repr(list(map(str, target))), repr(list(map(str, source))), ext, repr(self.keys())))
179 class CallableSelector(SCons.Util.Selector):
180 """A callable dictionary that will, in turn, call the value it
182 def __call__(self, env, source):
183 value = SCons.Util.Selector.__call__(self, env, source)
185 value = value(env, source)
188 class DictEmitter(SCons.Util.Selector):
189 """A callable dictionary that maps file suffixes to emitters.
190 When called, it finds the right emitter in its dictionary for the
191 suffix of the first source file, and calls that emitter to get the
192 right lists of targets and sources to return. If there's no emitter
193 for the suffix in its dictionary, the original target and source are
196 def __call__(self, target, source, env):
197 emitter = SCons.Util.Selector.__call__(self, env, source)
199 target, source = emitter(target, source, env)
200 return (target, source)
202 class ListEmitter(collections.UserList):
203 """A callable list of emitters that calls each in sequence,
204 returning the result.
206 def __call__(self, target, source, env):
208 target, source = e(target, source, env)
209 return (target, source)
211 # These are a common errors when calling a Builder;
212 # they are similar to the 'target' and 'source' keyword args to builders,
213 # so we issue warnings when we see them. The warnings can, of course,
215 misleading_keywords = {
216 'targets' : 'target',
217 'sources' : 'source',
220 class OverrideWarner(collections.UserDict):
221 """A class for warning about keyword arguments that we use as
222 overrides in a Builder call.
224 This class exists to handle the fact that a single Builder call
225 can actually invoke multiple builders. This class only emits the
226 warnings once, no matter how many Builders are invoked.
228 def __init__(self, dict):
229 collections.UserDict.__init__(self, dict)
230 if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
231 self.already_warned = None
233 if self.already_warned:
235 for k in self.keys():
236 if k in misleading_keywords:
237 alt = misleading_keywords[k]
238 msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
239 SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
240 self.already_warned = 1
243 """A factory for builder objects."""
245 if 'generator' in kw:
247 raise UserError("You must not specify both an action and a generator.")
248 kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'], {})
251 source_ext_match = kw.get('source_ext_match', 1)
252 if 'source_ext_match' in kw:
253 del kw['source_ext_match']
254 if SCons.Util.is_Dict(kw['action']):
255 composite = DictCmdGenerator(kw['action'], source_ext_match)
256 kw['action'] = SCons.Action.CommandGeneratorAction(composite, {})
257 kw['src_suffix'] = composite.src_suffixes()
259 kw['action'] = SCons.Action.Action(kw['action'])
262 emitter = kw['emitter']
263 if SCons.Util.is_String(emitter):
264 # This allows users to pass in an Environment
265 # variable reference (like "$FOO") as an emitter.
266 # We will look in that Environment variable for
267 # a callable to use as the actual emitter.
268 var = SCons.Util.get_environment_var(emitter)
270 raise UserError("Supplied emitter '%s' does not appear to refer to an Environment variable" % emitter)
271 kw['emitter'] = EmitterProxy(var)
272 elif SCons.Util.is_Dict(emitter):
273 kw['emitter'] = DictEmitter(emitter)
274 elif SCons.Util.is_List(emitter):
275 kw['emitter'] = ListEmitter(emitter)
277 result = BuilderBase(**kw)
279 if not composite is None:
280 result = CompositeBuilder(result, composite)
284 def _node_errors(builder, env, tlist, slist):
285 """Validate that the lists of target and source nodes are
286 legal for this builder and environment. Raise errors or
287 issue warnings as appropriate.
290 # First, figure out if there are any errors in the way the targets
294 raise UserError("Multiple ways to build the same target were specified for: %s" % t)
295 if t.has_explicit_builder():
296 if not t.env is None and not t.env is env:
297 action = t.builder.action
298 t_contents = action.get_contents(tlist, slist, t.env)
299 contents = action.get_contents(tlist, slist, env)
301 if t_contents == contents:
302 msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
303 SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
305 msg = "Two environments with different actions were specified for the same target: %s" % t
308 if t.builder != builder:
309 msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
311 # TODO(batch): list constructed each time!
312 if t.get_executor().get_all_targets() != tlist:
313 msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, list(map(str, t.get_executor().get_all_targets())), list(map(str, tlist)))
315 elif t.sources != slist:
316 msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, list(map(str, t.sources)), list(map(str, slist)))
319 if builder.single_source:
321 raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist))))
324 """This is a callable class that can act as a
325 Builder emitter. It holds on to a string that
326 is a key into an Environment dictionary, and will
327 look there at actual build time to see if it holds
328 a callable. If so, we will call that as the actual
330 def __init__(self, var):
331 self.var = SCons.Util.to_String(var)
333 def __call__(self, target, source, env):
336 # Recursively substitute the variable.
337 # We can't use env.subst() because it deals only
338 # in strings. Maybe we should change that?
339 while SCons.Util.is_String(emitter) and emitter in env:
340 emitter = env[emitter]
341 if callable(emitter):
342 target, source = emitter(target, source, env)
343 elif SCons.Util.is_List(emitter):
345 target, source = e(target, source, env)
347 return (target, source)
350 def __cmp__(self, other):
351 return cmp(self.var, other.var)
354 """Base class for Builders, objects that create output
355 nodes (files) from input nodes (files).
358 if SCons.Memoize.use_memoizer:
359 __metaclass__ = SCons.Memoize.Memoized_Metaclass
361 memoizer_counters = []
363 def __init__(self, action = None,
367 target_factory = None,
368 source_factory = None,
369 target_scanner = None,
370 source_scanner = None,
379 ensure_suffix = False,
381 if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
385 if SCons.Util.is_Dict(prefix):
386 prefix = CallableSelector(prefix)
388 if SCons.Util.is_Dict(suffix):
389 suffix = CallableSelector(suffix)
391 self.single_source = single_source
392 if 'overrides' in overrides:
393 SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
394 "The \"overrides\" keyword to Builder() creation has been deprecated;\n" +\
395 "\tspecify the items as keyword arguments to the Builder() call instead.")
396 overrides.update(overrides['overrides'])
397 del overrides['overrides']
398 if 'scanner' in overrides:
399 SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
400 "The \"scanner\" keyword to Builder() creation has been deprecated;\n"
401 "\tuse: source_scanner or target_scanner as appropriate.")
402 del overrides['scanner']
403 self.overrides = overrides
405 self.set_suffix(suffix)
406 self.set_src_suffix(src_suffix)
407 self.ensure_suffix = ensure_suffix
409 self.target_factory = target_factory
410 self.source_factory = source_factory
411 self.target_scanner = target_scanner
412 self.source_scanner = source_scanner
414 self.emitter = emitter
416 # Optional Builder name should only be used for Builders
417 # that don't get attached to construction environments.
420 self.executor_kw = {}
421 if not chdir is _null:
422 self.executor_kw['chdir'] = chdir
423 self.is_explicit = is_explicit
425 if src_builder is None:
427 elif not SCons.Util.is_List(src_builder):
428 src_builder = [ src_builder ]
429 self.src_builder = src_builder
431 def __nonzero__(self):
432 raise InternalError("Do not test for the Node.builder attribute directly; use Node.has_builder() instead")
434 def get_name(self, env):
435 """Attempts to get the name of the Builder.
437 Look at the BUILDERS variable of env, expecting it to be a
438 dictionary containing this Builder, and return the key of the
439 dictionary. If there's no key, then return a directly-configured
440 name (if there is one) or the name of the class (by default)."""
443 index = env['BUILDERS'].values().index(self)
444 return env['BUILDERS'].keys()[index]
445 except (AttributeError, KeyError, TypeError, ValueError):
448 except AttributeError:
449 return str(self.__class__)
451 def __cmp__(self, other):
452 return cmp(self.__dict__, other.__dict__)
454 def splitext(self, path, env=None):
458 suffixes = self.src_suffixes(env)
461 return match_splitext(path, suffixes)
463 def _adjustixes(self, files, pre, suf, ensure_suffix=False):
467 if not SCons.Util.is_List(files):
471 if SCons.Util.is_String(f):
472 f = SCons.Util.adjustixes(f, pre, suf, ensure_suffix)
476 def _create_nodes(self, env, target = None, source = None):
477 """Create and return lists of target and source nodes.
479 src_suf = self.get_src_suffix(env)
481 target_factory = env.get_factory(self.target_factory)
482 source_factory = env.get_factory(self.source_factory)
484 source = self._adjustixes(source, None, src_suf)
485 slist = env.arg2nodes(source, source_factory)
487 pre = self.get_prefix(env, slist)
488 suf = self.get_suffix(env, slist)
492 t_from_s = slist[0].target_from_source
493 except AttributeError:
494 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
498 splitext = lambda S: self.splitext(S,env)
499 tlist = [ t_from_s(pre, suf, splitext) ]
501 target = self._adjustixes(target, pre, suf, self.ensure_suffix)
502 tlist = env.arg2nodes(target, target_factory, target=target, source=source)
505 # The emitter is going to do str(node), but because we're
506 # being called *from* a builder invocation, the new targets
507 # don't yet have a builder set on them and will look like
508 # source files. Fool the emitter's str() calls by setting
509 # up a temporary builder on the new targets.
512 if not t.is_derived():
514 new_targets.append(t)
516 orig_tlist = tlist[:]
517 orig_slist = slist[:]
519 target, source = self.emitter(target=tlist, source=slist, env=env)
521 # Now delete the temporary builders that we attached to any
522 # new targets, so that _node_errors() doesn't do weird stuff
523 # to them because it thinks they already have builders.
524 for t in new_targets:
525 if t.builder is self:
526 # Only delete the temporary builder if the emitter
527 # didn't change it on us.
530 # Have to call arg2nodes yet again, since it is legal for
531 # emitters to spit out strings as well as Node instances.
532 tlist = env.arg2nodes(target, target_factory,
533 target=orig_tlist, source=orig_slist)
534 slist = env.arg2nodes(source, source_factory,
535 target=orig_tlist, source=orig_slist)
539 def _execute(self, env, target, source, overwarn={}, executor_kw={}):
540 # We now assume that target and source are lists or None.
542 source = self.src_builder_sources(env, source, overwarn)
544 if self.single_source and len(source) > 1 and target is None:
546 if target is None: target = [None]*len(source)
547 for tgt, src in zip(target, source):
548 if not tgt is None: tgt = [tgt]
549 if not src is None: src = [src]
550 result.extend(self._execute(env, tgt, src, overwarn))
551 return SCons.Node.NodeList(result)
555 tlist, slist = self._create_nodes(env, target, source)
557 # Check for errors with the specified target/source lists.
558 _node_errors(self, env, tlist, slist)
560 # The targets are fine, so find or make the appropriate Executor to
561 # build this particular list of targets from this particular list of
569 executor = tlist[0].get_executor(create = 0)
570 except (AttributeError, IndexError):
573 executor.add_sources(slist)
577 fmt = "Builder %s must have an action to build %s."
578 raise UserError(fmt % (self.get_name(env or self.env),
579 list(map(str,tlist))))
580 key = self.action.batch_key(env or self.env, tlist, slist)
583 executor = SCons.Executor.GetBatchExecutor(key)
587 executor.add_batch(tlist, slist)
590 executor = SCons.Executor.Executor(self.action, env, [],
591 tlist, slist, executor_kw)
593 SCons.Executor.AddBatchExecutor(key, executor)
595 # Now set up the relevant information in the target Nodes themselves.
597 t.cwd = env.fs.getcwd()
601 t.set_executor(executor)
602 t.set_explicit(self.is_explicit)
604 return SCons.Node.NodeList(tlist)
606 def __call__(self, env, target=None, source=None, chdir=_null, **kw):
607 # We now assume that target and source are lists or None.
608 # The caller (typically Environment.BuilderWrapper) is
609 # responsible for converting any scalar values to lists.
611 ekw = self.executor_kw
613 ekw = self.executor_kw.copy()
617 def prependDirIfRelative(f, srcdir=kw['srcdir']):
619 if SCons.Util.is_String(f) and not os.path.isabs(f):
620 f = os.path.join(srcdir, f)
622 if not SCons.Util.is_List(source):
624 source = list(map(prependDirIfRelative, source))
627 env_kw = self.overrides.copy()
632 env_kw = self.overrides
633 env = env.Override(env_kw)
634 return self._execute(env, target, source, OverrideWarner(kw), ekw)
636 def adjust_suffix(self, suff):
637 if suff and not suff[0] in [ '.', '_', '$' ]:
641 def get_prefix(self, env, sources=[]):
644 prefix = prefix(env, sources)
645 return env.subst(prefix)
647 def set_suffix(self, suffix):
648 if not callable(suffix):
649 suffix = self.adjust_suffix(suffix)
652 def get_suffix(self, env, sources=[]):
655 suffix = suffix(env, sources)
656 return env.subst(suffix)
658 def set_src_suffix(self, src_suffix):
661 elif not SCons.Util.is_List(src_suffix):
662 src_suffix = [ src_suffix ]
663 self.src_suffix = [callable(suf) and suf or self.adjust_suffix(suf) for suf in src_suffix]
665 def get_src_suffix(self, env):
666 """Get the first src_suffix in the list of src_suffixes."""
667 ret = self.src_suffixes(env)
672 def add_emitter(self, suffix, emitter):
673 """Add a suffix-emitter mapping to this Builder.
675 This assumes that emitter has been initialized with an
676 appropriate dictionary type, and will throw a TypeError if
677 not, so the caller is responsible for knowing that this is an
678 appropriate method to call for the Builder in question.
680 self.emitter[suffix] = emitter
682 def add_src_builder(self, builder):
684 Add a new Builder to the list of src_builders.
686 This requires wiping out cached values so that the computed
687 lists of source suffixes get re-calculated.
690 self.src_builder.append(builder)
692 def _get_sdict(self, env):
694 Returns a dictionary mapping all of the source suffixes of all
695 src_builders of this Builder to the underlying Builder that
696 should be called first.
698 This dictionary is used for each target specified, so we save a
699 lot of extra computation by memoizing it for each construction
702 Note that this is re-computed each time, not cached, because there
703 might be changes to one of our source Builders (or one of their
704 source Builders, and so on, and so on...) that we can't "see."
706 The underlying methods we call cache their computed values,
707 though, so we hope repeatedly aggregating them into a dictionary
708 like this won't be too big a hit. We may need to look for a
709 better way to do this if performance data show this has turned
710 into a significant bottleneck.
713 for bld in self.get_src_builders(env):
714 for suf in bld.src_suffixes(env):
718 def src_builder_sources(self, env, source, overwarn={}):
719 sdict = self._get_sdict(env)
721 src_suffixes = self.src_suffixes(env)
723 lengths = list(set(map(len, src_suffixes)))
725 def match_src_suffix(name, src_suffixes=src_suffixes, lengths=lengths):
726 node_suffixes = [name[-l:] for l in lengths]
727 for suf in src_suffixes:
728 if suf in node_suffixes:
733 for s in SCons.Util.flatten(source):
734 if SCons.Util.is_String(s):
735 match_suffix = match_src_suffix(env.subst(s))
736 if not match_suffix and not '.' in s:
737 src_suf = self.get_src_suffix(env)
738 s = self._adjustixes(s, None, src_suf)[0]
740 match_suffix = match_src_suffix(s.name)
743 bld = sdict[match_suffix]
747 tlist = bld._execute(env, None, [s], overwarn)
748 # If the subsidiary Builder returned more than one
749 # target, then filter out any sources that this
750 # Builder isn't capable of building.
752 tlist = [t for t in tlist if match_src_suffix(t.name)]
757 source_factory = env.get_factory(self.source_factory)
759 return env.arg2nodes(result, source_factory)
761 def _get_src_builders_key(self, env):
764 memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
766 def get_src_builders(self, env):
768 Returns the list of source Builders for this Builder.
770 This exists mainly to look up Builders referenced as
771 strings in the 'BUILDER' variable of the construction
772 environment and cache the result.
776 memo_dict = self._memo['get_src_builders']
779 self._memo['get_src_builders'] = memo_dict
782 return memo_dict[memo_key]
787 for bld in self.src_builder:
788 if SCons.Util.is_String(bld):
790 bld = env['BUILDERS'][bld]
795 memo_dict[memo_key] = builders
798 def _subst_src_suffixes_key(self, env):
801 memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
803 def subst_src_suffixes(self, env):
805 The suffix list may contain construction variable expansions,
806 so we have to evaluate the individual strings. To avoid doing
807 this over and over, we memoize the results for each construction
812 memo_dict = self._memo['subst_src_suffixes']
815 self._memo['subst_src_suffixes'] = memo_dict
818 return memo_dict[memo_key]
821 suffixes = [env.subst(x) for x in self.src_suffix]
822 memo_dict[memo_key] = suffixes
825 def src_suffixes(self, env):
827 Returns the list of source suffixes for all src_builders of this
830 This is essentially a recursive descent of the src_builder "tree."
831 (This value isn't cached because there may be changes in a
832 src_builder many levels deep that we can't see.)
835 suffixes = self.subst_src_suffixes(env)
838 for builder in self.get_src_builders(env):
839 for s in builder.src_suffixes(env):
845 class CompositeBuilder(SCons.Util.Proxy):
846 """A Builder Proxy whose main purpose is to always have
847 a DictCmdGenerator as its action, and to provide access
848 to the DictCmdGenerator's add_action() method.
851 def __init__(self, builder, cmdgen):
852 if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
853 SCons.Util.Proxy.__init__(self, builder)
855 # cmdgen should always be an instance of DictCmdGenerator.
857 self.builder = builder
859 def add_action(self, suffix, action):
860 self.cmdgen.add_action(suffix, action)
861 self.set_src_suffix(self.cmdgen.src_suffixes())
863 def is_a_Builder(obj):
864 """"Returns True iff the specified obj is one of our Builder classes.
866 The test is complicated a bit by the fact that CompositeBuilder
867 is a proxy, not a subclass of BuilderBase.
869 return (isinstance(obj, BuilderBase)
870 or isinstance(obj, CompositeBuilder)
875 # indent-tabs-mode:nil
877 # vim: set expandtab tabstop=4 shiftwidth=4: