Hi Bill
First sorry I double posted this.. the first case I did not finish, fumbled a
key and posted before I was ready… This version should be “better”.
Given Parts makes components I needed a way to share data. I tried the Export
Import logic, but had issues with it. In the end I used Import to pass in the
env and other functions I add in Parts.
This is why all .parts files start with Import(‘*’). I could get around this by
taking more control, but this seemed to be counter productive to my hope of
getting Parts logic into SCons.
So to pass data around and to request what data is needed we have a DependsOn()
function ( and a Exportxxx()/SDKxxx functions that push values to be shareable”
that allows the user define they depend on a component with certain requires,
like version or other features ( given more than on Part is defined in the
SConstruct as it common if you cross build in a single pass). I this call you
can define some values that you require from that component via the REQ object.
Certain values are passed automatically such as CPPPATH, and LIBS.. ie values
you need to compile against the component, while other values need to be
explicitly requested, such as CPPFLAGS or custom values that may have been
exporting from the Component. When you DependOn([Component(A)) Parts will add
some strings to map implicated values. These may not get expanded at all and at
worse waste some space in the environment. Explicit value defined by the REQ
meta object also get mapped. For this e-mail, I will keep it simple and leave
out some features.
The main point is that the string we add is something like
“{PARTIDEXPORTS(‘sub1@version:1.*’,’build’,’LIBS’,’1’)}”
This call a parts.mapper object ( we have a few defined in Parts). This object
will look up the needed object and get the requested data in the dependent
component and add it to the environment variable in question. This subst
happens when SCons subst the code. We do some tricks to avoid extra look ups
and to replace values that are known. This way Part A can depend on Part B and
C and we can add to the LIBS and LIBSPATH variable the needed information
without the developer bothering to do this. What is also nice about this is
that if B was making a Boo.lib files and it changes to a BOB1.2.lib file, the
only change needed is the in B.part files. The change will auto propagate
correctly up the depends chain and everything will just rebuild. As I stated
this happens via calling a python object in the subst() call and having the
code correctly return a value and or a value and update in place a value in the
list ( as is common with CPPFLAGS, LIBS, etc..) In the end, you do a build (
and you can look at samples in Parts to see this happening) when the part file
is done being read we might see something like:
Given a setup such as
a->[B,C]
B->[]
C->[D]
Env[‘LIBS’] -> [‘a.lib’,’ {PARTIDEXPORTS(‘B@version:2.*’,’build’,’LIBS’,’1’)}’,
{PARTIDEXPORTS(‘C@version:1.*’,’build’,’LIBS’,’1’)}”]
But when the subst for like “LINKCOM” happens it will get for the $LIBS a list
like -> [‘a.lib’,’b.lib’,’c.lib’, ’d.lib’]. technically this happens with the
Scanner first for items LIBS, LIBPATH, CPPPATH, so when the command subst
happens we already replaced ourselves to prevent redundant logic from happening.
The mapper code in all the glory can be seen here:
https://bitbucket.org/sconsparts/parts/src/6a57ea6ae48acf491cda5ab8c1d45fd53d4543c2/parts/mappers.py?at=master&fileviewer=file-view-default
If you look at the history this was simpler code at first, but then Gary
complained I made it to C\C++ specific so it could never get into SCons, I
tweaked to make it more general, then I needed to make it better to allow
custom values. Other tweaks have been made as well over time to address bugs
or feature issues. The issue is that this code goes off a lot and takes time to
process in Large builds. Work has been done to replace the SCons version of
_concat_ixes(), and some other objects to make them not faster. The last major
update we had made was to try to fix an issue in which we had a Vtune with some
messed up depends circular depends in which the mapper code would flip an
include path around with mid-level dependent components, cause a rebuild
because SCons saw a change in the environment. This code has been a sore spot
for me to a large degree as it never quite worked right in certain complex
cases was done to delay subst values because we would not know at load time
which variant of Part C you needed to get value from until we loaded all the
components. The current form is a bit complex and ugly honestly. There is some
odd work around for some odd behaviors with CmdStringHolder class.
Given what I have learned... The plan is to replace this given I get time to do
this via implementing:
1. A new Parts format using @decorators to delay process functions ( ie make
the Parts file loading not an exec off a lot of python code, but a register
callback) This will allow me to load all the “parts” and have needed
information such as name, version, and other platform information before I try
to process what the component will emit as build actions.
2. For the existing format implement a “continuous loader”.. or load a Parts
file until I get to a DependsOn statement. At the point, I pause and start
loading another Part until all the primary “top” level Parts are loaded. At
that point, I know enough information to know what is at the bottom and what is
at the top… This can be done with threads in earlier versions of python or with
python3 (3.5 in my head) asyncio and yield “task” logic.
In both of these cases, this allows me to delay “processing” the expensive
stuff, such as env.Program([list of 1000 files]) until I really need to because
I know it is the build “target” list. I also can on first pass ( ie no DB) can
load the Parts in order and insert the needed values directly. Ie add “B.lib”
or “b/includes” to the needed item. I don’t need to subst and code. At most I
might need to move some values around, such as libs to make sure the linker is
happy and we avoid double ( or more) inserts of the same values. This would
allow Parts to start SCons up much faster as we can:
1. Avoid loading all the components if the target is to not build “all” or
“.”
2. Avoid a lot of subst() calls during the build allowing SCons, while
allowing me a nice way to share data in an organized way. This will also allow
me to share objects between components vs only strings. This will be good as it
will allow a “part” to define a builder and export it to be shared by other
components. Which is a more common request than I thought. I have ugly work
around at the moment to do this today.. but I rather not speak about them 😊
3. Independent of mappers, this allows for other speed ups such as possible
avoiding of code execution such as env.Builder([lots of objects]) what would
not be called or better yet avoid calls to SCons Glob() or the Parts Pattern()
which scan the disk for files. This was found in profiling to be one of the
biggest reasons why it took the time to load a part. We had cases such as boost
in which a few scans happened ( one for headers to export and few different
ones for sources to build different boost components) which would result in the
file taking 30 seconds to load not average .02 – .5 seconds on a laptop
spinning disk the other parts generally took that did not scan large number of
files. ( this is one reason for the use the cache better argument I made
before, as we should only need to scan a directory at most once, and then only
again for a change. I had a prototype this in Parts once and it showed a big
improvement in load time for the read phase.. never was finished it ☹, but for
me the point is that once we know what it is we should save it and not
reproduce calculating it until something says we need too. File and directory
nodes, and subst string values I had used to pass libs, paths and other values
between components are great examples of this )
I hope this helps… let me know if you have other questions.
You can also install Parts with SCons on a box and run a some of my samples..
add a --trace=mappers should give you a big dump of data. ( ie add --verbose
and --trace will dump everything… adding the [category] printed with a verbose
or trace message to the –trace or –verbose separated with commas will allow you
to filter to a smaller set of data. It will help show what is going on and how
Parts shares data
Jason
In case you want a simple example of what this might look like without playing
with code
# hello part depends on print_msg parts
PartName('hello')
env.DependsOn([Component('print_msg','1.*')])
ret=env.Program("hello","hello.c")
env.InstallTarget(ret)
------------
# the print_msg.part
PartVersion('1.0.0')
PartName('print_msg')
#files
cpp_files=['print_msg.c']
…
outputs=env.SharedLibrary('print_msg',cpp_files)
#This will auto export the print_msg.lib file as well other stuff related to
install sandbox and possible package creation
env.InstallTarget(outputs)
# this exports the print_msg.h include path
env.SdkInclude(['print_msg.h'])
---
#SConstruct
from parts import *
Part('hello/hello.parts')
Part('print_msg/print_msg.parts')
_______________________________________________
Scons-dev mailing list
[email protected]
https://pairlist2.pair.net/mailman/listinfo/scons-dev