Cloning public code: An example

For some strange reason, people are still changing public code and doing build and sysgen's on their Windows CE tree (read this to learn why this is a bad thing). Changing code in the PUBLIC and PRIVATE trees might seem like a shortcut but actually it is everything but a shortcut. When you clone the code before changing it in place you will save yourself hours and hours of build time. A targeted build of a cloned driver takes seconds, a clean build and sysgen takes hours.

Cloning is not difficult at all, so there is really no reason to change PUBLIC or PRIVATE code. To prove that it is not difficult (once you know what you are doing) I will show you how to clone PUBLIC code in this blog post. Note that there is an article by Steve Maillet and Mike Hall in MSDN about cloning public code too. They split the clone into a "LIB" part and a "DLL" part. I don't really like that method; I just merge the sources file into one that creates the DLL in one go. Have a look at the "split" method here.

I chose to clone NETUI, since this component is responsible for a lot of dialog boxes that you may want to change to match your systems specifications.

I am cloning NETUI on a Windows CE 6.0 tree, but it works exactly the same on a Windows CE 5.0 tree (apart from the OSDesign folder which is called PBWorkspaces in CE 5.0).

Prerequisites: A sysgenned OSDesign with the "Network UI" component included.

Let's start!

  1. Copy the entire \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\NETUI folder to your OSDesign, for instance \WINCE600\OSDesigns\MyOS\MyOS
  2. Open a build window (in Platform Builder / VS2005 click "Open Release Directory in Build Window" from the menu "Build"
  3. In the build window, type:
    cd..
    cd..
    cd netui
  4. Now type:
    sysgen_capture -p common netui

    This command will output a set of files in the current directory.

  5. Since we only want to build netui, you can delete:
    sources.btdrt
    sources.ceddk
    sources.iphlpapi
    sources.k.iphlpapi
    sources.ws2
    sources.ws2k
  6. Now intelligently merge sources. with sources.netui (we make the changes in the sources. file):
    • Make a backup of the sources. file (copy to sources.org) so you can always go back if needed
    • We are trying to build a DLL, so change TARGETTYPE=LIBRARY to TARGETTYPE=DYNLINK
    • We are building NETUI as a project in our workspace, so set the RELEASETYPE to LOCAL (RELEASETYPE=LOCAL)
    • We are not building a LIB anymore, so there's no need to preprocess the def file. Remove the following lines:
      WINCETARGETFILE0=$(_RELEASELIBDIR)\$(TARGETDEFNAME).def
      PREPROCESSDEFFILE=1
    • And add this line to tell the build system to use the local def file for the exports:

      DEFFILE=$(TARGETDEFNAME).def
    • We don't need the resource file later (because we are not creating a lib that has to be transformed into a dll later), so remove the COPYRES=1 line and the WINCETARGETFILES line pointing to the res.
    • OSDesign SubProjects get build last, so we don't need to copy SYNCHRONIZE_DRAIN=1 from sources.netui to our sources. file
    • Since we are now building a DLL, copy the DLLENTRY line from sources.netui to sources.
    • Now move the entire SOURCELIBS and TARGETLIBS from sources.netui to sources.
    • and delete any references to netui (netui.lib/netui.res) itself (because we are building that now)
    • Change the paths to the target libraries from using the PUBLIC sysgened libraries to the libraries sysgened to your workspace, so change $(_SYSGENSDKROOT)\ to $(_PROJECTROOT)\cesysgen\sdk\ and $(_SYSGENOAKROOT)\ to $(_PROJECTROOT)\cesysgen\oak\
    • You can also delete the #xref lines, they are remains of older days (CE 2.11 era).
    • Now try to build (by typing "build" in the build window you still have open)
    • oops! Doesn't build. It can't find the standard include files... Add the following to the top of the sources file:
      _ISVINCPATH=$(_WINCEROOT)\public\common\sdk\inc;
      _OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc; $(_WINCEROOT)\public\common\oak\inc; $(_WINCEROOT)\public\common\sdk\inc;

      NOTE: DO NOT PUT SPACES BETWEEN THE PATHS! I know it is confusing because I do it above, but I have to to support line breaking (otherwise this page will look ugly...).

    • And since this project is building OEM code (a kernel component) set WINCEOEM=1 to indicate we want to use the _OEMINCPATH (so if you really want to you can delete the _ISVINCPATH since we don't use that one).
    • Try to build again:
      BUILD: [01:0000000048:ERRORE] \WINCE600\OSDesigns\MyOS\MyOS\NETUI\.\btmgmtui.cpp(39) : fatal error C1083: Cannot open include file: '../bluetooth/sample/btenum/btenum.hxx': No such file or directory

      So, one of the sources files is trying to include a header on a relative path. Since NETUI came from \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\NETUI this path must be \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\BLUETOOTH

      Now there are 3 ways to solving this: Either add the \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\NETUI folder to the include path (add INCLUDES=$(INCLUDES);\WINCE600\PUBLIC\COMMON\OAK\DRIVERS\NETUI) so that the header file can be found off the relative path, or change the source file including the header to use a full path to the header file, or copy the btenum.hxx file into the NETUI folder and change the source file including the header to use the local header file. I think the last option is the cleanest, so let's copy \WINCE600\PUBLIC\COMMON\OAK\DRIVERS\BLUETOOTH\SAMPLE\BTENUM\btenum.hxx to \WINCE600\OSDesigns\MyOS\MyOS\NETUI\btenum.hxx and change btmgmtui.cpp to #include "btenum.hxx"

    • Build again: 0 Warnings,  0 Errors. Whuuhooo!
    • Now there is one final thing we have to add to the NETUI sources. file. We have to tell the build system we want to copy the final binary to the _FLATRELEASEDIR so our netui.dll will overwrite the default one from the PUBLIC\COMMON folder. Add WINCEREL=1 to the top of the sources. file.
  7. Now add the NETUI project to your workspace. In the Platform Builder VS IDE click "Add Existing Subproject..." from the "Project" menu. Browse to the \WINCE600\OSDesigns\MyOS\MyOS\NETUI folder, select "Sources/Dirs Files" from the "Files of type" dropdown list, select the sources. file, and click "Open"
  8. In the Solution Explorer window you can now find your NETUI component under the "SubProjects" node. Right click NETUI and click "Rebuild" to see if you can build the project from the IDE. It should all build fine.
  9. If all builds fine, you can delete the sources.netui and the sources.org files since you no longer need them now.
  10. The "Insert existing project" action created a couple of files for you. One of those files is a bib file. BIB files are used to indicate which files need to be included in the image. Since we cloned NETUI, but did not remove it from the OSDesign features, we now have 2 references to NETUI in the combined BIB files (one is in COMMON.BIB and the other in our newly generated NETUI.BIB). We don't need an extra reference in the BIB files, all we want is overwrite the default NETUI.DLL in the _FLATRELEASEDIR, so you can remove this reference from the newly generated NETUI.bib.

Now you can change whatever you need to change in NETUI without worrying about messing up the PUBLIC portion of the WINCE tree or having to do a "Clean Build and Sysgen".

If you have followed all the steps above your final NETUI sources. file should look like this (except the spaces in the _ISVINCPATH, _OEMINCPATH and INCLUDES macros!):

_ISVINCPATH=$(_WINCEROOT)\public\common\sdk\inc;
_OEMINCPATH=$(_WINCEROOT)\public\common\ddk\inc; $(_WINCEROOT)\public\common\oak\inc; $(_WINCEROOT)\public\common\sdk\inc;

WINCEOEM=1
WINCEREL=1

TARGETNAME=netui
TARGETTYPE=DYNLINK
RELEASETYPE=LOCAL

TARGETDEFNAME=$(TARGETNAME)
DEFFILE=$(TARGETDEFNAME).def

DLLENTRY=_DllEntryCRTStartup

CONDITIONAL_INCLUDES=prshtp.h

SOURCELIBS=

TARGETLIBS=\
    $(_PUBLICROOT)\common\oak\lib\$(_CPUINDPATH)\btenum.lib \
    $(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\iphlpapi.lib \
    $(_PROJECTROOT)\cesysgen\oak\lib\$(_CPUINDPATH)\btdrt.lib \
    $(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\ws2.lib \
    $(_PROJECTROOT)\cesysgen\oak\lib\$(_CPUINDPATH)\ceddk.lib \
    $(_PROJECTROOT)\cesysgen\sdk\lib\$(_CPUINDPATH)\coredll.lib

SOURCES= \
    netui.c \
    getip.c \
    ipaddr.c \
    getuser.c \
    linecnfg.c \
    wnet.c \
    netcard.c \
    certui.cpp  \
    showcert.cpp \
    eaptlscfg.cpp \
    network.c \
    transdlg.c \
    reg.c \
    util.c \
    wzcui.c \
    wzcprops.c \
    btmgmtui.cpp \
    WzcQuickCfgUi.c \
    IpQuickCfgUi.c \
    QuickConfigUi.c \
    WzcLogging.c \
    WzcPopup.c \
    netui.rc  \
    wnet_wrapper.cpp \
    netui_wrapper.cpp \
    getuser_wrapper.cpp \
    gwes_wrapper.cpp \
    getip_wrapper.cpp \
    linecnfg_wrapper.cpp \
    transdlg_wrapper.cpp \
    network_wrapper.cpp \
    netcard_wrapper.cpp

FILE_VIEW_PARAMETER_FOLDER= \
    NETUI.bib \
    NETUI.reg \
    NETUI.dat \
    NETUI.db \
    ProjSysgen.bat \

FILE_VIEW_ROOT_FOLDER= \
    prelink.bat \
    postlink.bat \

PRELINK_PASS_CMD=prelink.bat
POSTLINK_PASS_CMD=postlink.bat

Using Catalog to clone

I have tried cloning Public\Common\OAK\Drivers\PCIBUS using two different methods:
1). via the MSDN article from Steve Maillet and Mike Hall, and
2). from Catalog Items View (CE6, Visual Studio 2005 SP1), right-click on "PCI Bus" item under device drivers/bus drivers and select "Clone Catalog Item".

Method 1 eventually worked, though I had to fool around with the sources files more than suggested in their article. This method does not create a sub-project in PB/VS. They have you puts things under _TARGETPLATROOT instead.

Method 2 creates a sub-project (two in fact--as it also creates a DefBusLib project) and seems more closely related to how you suggest cloning a project. However, simply building after cloning results in actually crashing the build (not just a build fail--it actually crashes the PB build component). It appears this clone option only does a portion of the work, though I haven't tracked down what is wrong yet. However, I note that it renames the libraries from their original name to with "_clone" appended.

Q1: what are the advantages/disadvantages of locating cloned drivers as a sub-project in OSDesigns vs. _TARGETPLATROOT?

Q2: does anyone actually use the catalog PB clone option for drivers, or would I end up needing to perform almost all the steps you show above anyway?

Q3: do you recommend renaming cloned drivers?

BTW: if anyone is curious, this is the error from method 2: "Platform Builder Platform Builder Dependency Discovery has encountered a problem and needs to close. We are sorry for the inconvenience."

Thanks for the instructions above--I'm going to try that as method 3.

RE: Using Catalog to clone

A1: Advantages are that it is a bit easier to maintain, but that also largely depends on whether you are the developer of the BSP, or are using a BSP from an OEM to develop just an image

A2: I've tried it some time ago, and found it simply doesn't work.

A3: No. Leave the original names in tact. That way you can keep the catalog item in your OS Design (which keeps all sysgen vars set etc) and you can simply concentrate on the changes you want without having to set all those vars as well. Your driver as either a subproject or in your BSP will be linked after the sysgenned libs and thus your driver (with the changes) will overwrite the original one in the FLATRELEASEDIR.

Good luck!

Michel Verhagen

Cloning Netui sources code and resource

Hello Michael,
I am used to clone code but this time I have done some changes also in the resource files. My problem right now is that it is keep using the default netui.res. What do I have to do to get the modified resource in my image? Do I need to add a postbuild.bat and copy the netui.res from my cloned \NETUI\obj\ARMV4I\retail\ folder to %_FLATRELEASEDIR%\0409\ folder?

Thank you,

Well... it did work when I

Well... it did work when I cloned parallel driver, but now when I'm trying to clone WINCE600\PLATFORM\COMMON\SRC\X86\COMMON\INTR to add my own ISR for IRQ7 I get error while making "sysgnen_capture -p common intr".

What I can see in build.log is:

"Starting sysgen phase for project ( common )
Found localized resources for Languages ( 0404 0407 0409 040C 0410 0411 0412 0413 0416 0419 041D 0804 0C0A)

Microsoft (R) Program Maintenance Utility Version CE-6.00.1893.0
Copyright (C) Microsoft Corporation. All rights reserved.

Windows CE Version (Release) (Built on Jun 30 2006 16:52:46)
makefile.def: Invoked with predefined settings:
TARGETNAME: dummy
TARGETTYPE: dummy
RELEASETYPE:
TARGETLIBS:
SOURCELIBS:
makefile.def: Including D:\WINCE600\public\common\oak\misc\Sources.default
makefile.def: BUILDROOT is D:\WINCE600\public\common\cesysgen
makefile.def: Including D:\WINCE600\PUBLIC\COMMON\CESYSGEN\sources
makefile.def: Including D:\WINCE600\public\common\oak\misc\Sources.CE
Directory: D:\WINCE600\PUBLIC\COMMON\CESYSGEN
TARGETNAME: dummy
RELEASETYPE is not defined. Using DEFAULT.
makefile.def: Including D:\WINCE600\public\common\oak\misc\sources.ReleaseType_DEFAULT
NMAKE : fatal error U1073: don't know how to make 'intr'
Stop."

What is wrong?

RE: Well... it did work when I

In this case all you want is modify this LIB. So follow the following steps:

Copy the INTR folder to your BSP.
Change (or add) the RELEASETYPE in the sources file of INTR in your BSP to "PLATFORM" (now the lib will output to \WINCEX00\PLATFORM\YourBSP\lib\...
Now make sure that all other sources. files in your BSP that link oal_intr_x86.lib now point to your local BSP version of that lib instead of the PLATFORM\COMMON one.

Thanks for your answer, I

Thanks for your answer, I have two more questions:
1. Where should I copy INTR directory if I am using default x86 CEPC? Should it be just WINCE600\PLATFORM\CEPC or somewhere deeper?
2. How can I check which files link oal_int_x86? Should I check all the "sources" in all subdirectories of WINCE600\PLATFORM\CEPC ?

RE: Thanks for your answer

First of all, you did not develop the CEPC BSP so you should not change any code in place. First clone the CEPC BSP (using the Clone BSP command from the Platform Builder menu), then copy INTR to \PLATFORM\YourBSP\SRC\... and modify dirs to build your INTR version. Whatever folder you like. Just make sure it gets build before any library that uses the lib INTR makes (see SYNCHRONIZE_DRAIN and SYNCHRONIZE_BLOCK).

To answer no 2: Yes.

To include the dll in the final image

Hi Michel,

Thanks for the detailed procedure.

After cloning public code to platform\drivers, when I give build at command line, it compiles successfully and generates the dll in the release directory. Now what should I give at the command line to make sure the final image includes the generated dll? Does makeimg alone take care?

Thanks,
Priya

RE: To include the dll in the final image

Make sure WINCEREL=1 is set in your sources (or sources.cmn) file so the file is copied to the FLATRELEASEDIR automatically. If the component you cloned is linked to a catalog item that is already in your workspace there is nothing you have to do (apart from building your cloned DLL and do a makeimg). Your cloned DLL will overwrite the original DLL in your FLATRELEASEDIR and all should be fine. If the DLL you cloned is not linked to a component already in your workspace you can either add the component to your workspace or copy the registry entries for the component from its registry file (like common.reg or some local registry file used by the original component) to platform.reg and add an entry for the DLL in platform.bib, then do a sysgen in your BSP folder, followed by a buildrel (to sysgen and copy the modified platform.reg and platform.bib to the FLATRELEASEDIR) and finish with a makeimg to create a kernel containing your cloned DLL (and its registry settings).

How to copy ceshapi code?

Hi Michel,
Can you explain how to make a copy of ceshapi (public\shell\oak\hpc\ceshell\api) code to local project directory? I've followed your steps to do it but failed.
Below is my current work of sources file, it will end up with "cl : Command line error D2004 : '/I' requires an argument".

_ISVINCPATH=$(_COMMONSDKROOT)\inc
_OEMINCPATH=$(_COMMONSDKROOT)\inc; \
$(_COMMONOAKROOT)\inc; \
$(_COMMONDDKROOT)\inc

Thanks so much.

TARGETTYPE=LIBRARY
TARGETNAME=ceshapi
RELEASETYPE=PLATFORM
WINCEREL=1
PREPROCESSDEFFILE=1
TARGETDEFNAME=ceshell
DEFFILE=$(TARGETDEFNAME).def
WINCETARGETFILE0=$(_RELEASELIBDIR)\$(DEFFILE)

RCADDNULL=1
WINCETARGETFILES=$(_RELEASELIBDIR)\$(TARGETNAME)_base.res \
$(_RELEASELIBDIR)\$(TARGETNAME)_vgal.res \
$(_RELEASELIBDIR)\$(TARGETNAME)_qvgap.res

WINCEOEM=1
WARNLEVEL=4

INCLUDES=$(INCLUDES); $(_PUBLICROOT)\shell\oak\hpc\inc; \
$(_PUBLICROOT)\shell\oak\hpc\ceshell\inc; \
$(_PUBLICROOT)\shell\oak\hpc\ceshell\ui

SOURCES=api.cpp \
browse.cpp \
cundo.cpp \
drag.cpp \
fileopen.cpp \
iconcache.cpp \
idlist.cpp \
malloc.cpp \
shfileop.cpp \
shortcut.cpp \
recbin.cpp

RE: How to copy ceshapi code?

Command line error D2004 : '/I' requires an argument".

Means you've got an error in your INCLUDES macro. When you get the above error it's almost always because you have a space somewhere or a double ';', and in your case this is true as well. Change the INCLUDES, _ISVINCPATH and _OEMINCPATH to:

_ISVINCPATH=$(_COMMONSDKROOT)\inc;
_OEMINCPATH=$(_COMMONSDKROOT)\inc;$(_COMMONOAKROOT)\inc;$(_COMMONDDKROOT)\inc;

INCLUDES=$(INCLUDES);$(_PUBLICROOT)\shell\oak\hpc\inc;$(_PUBLICROOT)\shell\oak\hpc\ceshell\inc;$(_PUBLICROOT)\shell\oak\hpc\ceshell\ui;

In other words; remove the spaces!

I know I use spaces in the above examples. I did that to fix line breaking in the HTML pages. I've now added warnings in the blog text to make it clear you should not have spaces in those macro's. I apologize for the confusion!

Hi Michel, Appreciate very

Hi Michel,
Appreciate very much! After removing the white spaces, the problem is resolved! Thanks again!

'\I' requires an extra argument

I don't know where the build command is getting that extra \I from. Do you know where I could find it ?

RE: '\I' requires an extra argument

This error usually means you have a misformed INCLUDES macro in your sources file. The path behind the INCLUDES macro can't have any spaces in them and paths must be separated by one ';' and without any spaces between the paths as well, so like this:

INCLUDES=C:\path1\path2;c:\path3

If you use:

INCLUDES=$(INCLUDES);c:\path2

then make sure that the INCLUDES variable (set for instance by sources.cmn) doesn't have a trailing ';' (because that would mean the path would end up like this INCLUDES=c:\pathinsourcescmn;;c:\path2.

In short: There's something wrong with your INCLUDES macro.

If this is easy, I'd hate to

If this is easy, I'd hate to see hard!

MS needs to create a utility to do this.

RE: If this is easy, I'd hate to

That's why WE are here, if it's too hard, call us! ;)