views:

210

answers:

6

Delphi 2007/2009 odd question here:

It's possible, based on a component property defined in design-time, to include files in linking or leave them ?

Example: If I leave SomeProperty true, when compiling, the unit SomeUnit will be included into my project. Otherwise it will not be included.

My second approach to this problem is to deploy a second component, which when dropped in the form (or not) will include the unit in uses clause. But if it can be done with a property, that'll be better.

I want to avoid conditional compilation via IFDEF because that forces the component to be built every time the projects are built. Or not?

I am trying to achieve an easy way of including some units in project, and then those units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.

edit: I'll stay with the component way for instance. I knew the IFDEF method and things, but that forces the component to be built everytime the projects are built. Or not?

I was trying to achieve an easy way of including some units in project, and then that units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.

+6  A: 

No.

What are you trying to solve?

You could add a postcompiling step that would optionally include some resource based on a component property - but you'd have to do some coding to implement such a feature.

gabr
+3  A: 

Write an IDE add-in. Handle the "before compile" notification and check whether any forms or data modules in the project have components of the type you're interested in, and then check their properties. Based on what you find there, you can try modifying the contents of a unit to use the other unit of your choice. It certainly doesn't sound easy, but it seems possible.

Your second idea is very easy. It's exactly what the TXPManifest component does, for example. Beware that removing such a component from a form does not "unuse" the associated unit.

To conditionally add support for different databases, you might consider using run-time packages. (That's how the IDE manages to support so many different kinds of components, after all.) Put each database's custom code into a different package. Then, the databases you support are simply whichever ones have packages available at run time. No compile-time or design-time configuration required. The obstacle to this, however, is to manage which packages are available, and determine which of them are the packages that provide database support.

Rob Kennedy
+4  A: 

You can use the {$IFDEF youridentifier} optional code {$ENDIF} method to conditionally compile data in to your application and then to enable it just go to your project options and enter youridentifier into the appropriate option field. Another method of doing this is to add the following to the top of your unit (or in an include file):

{$DEFINE youridentifier}

which will force youridentifier on. To disable, just place a period right before the $:

{.$DEFINE youridentifier}

Using these techniques its easy to conditionally bring in code or replace code with each compile.

skamradt
+1, this seems like the best way to conditionally include files. It has the added benefit of being usable for command line builds / automatic builds.
mghie
+1  A: 

Your second approach will not necessarily work the way you want it to. While Delphi will helpfully add the necessary unit to your uses list when you drop the component onto the form, it will not remove the unit when you delete the component. And even if you don't use the component or any other exported entitiy from that unit, it is possible that the unit will be linked to your application anyway, when there is code in the initialization or finalization part of the unit. This is unfortunately very often the case, even when it would be possible to initialize stuff on-demand.

mghie
+1  A: 

There is no way to do what you are asking, but something you might not be aware of is that units which are included in your uses list but never referenced will have a minimal impact on the size of your executable. The smart linker that is in Delphi does a very good job of removing code which is never used. If you are careful about your "optional unit" which is referenced by the component, and don't have any code in it which is executed globally (everything is self contained in a class or classes) then it shouldn't matter if it is in the uses clause and not used, or not in the uses clause at all.

This would easily allow you to do what I think your wanting to do, which would be to drop a component on a form which includes a unit that then can be linked to your application. Removing the component would have the effect of not linking in the unit. I believe, however, that any resources (for example forms or other items included by the $R directive) which are in the used unit would still be included in the executable.

skamradt
A: 

You could use DesignIntf.RegisterSelectionEditor to register a selection editor (see comments in Delphi source code about ISelectionEditor), then use the RequiresUnits procedure to include extra units in uses clause.

TMySelectionEditor = class(TSelectionEditor)
public
  procedure RequiresUnits(Proc: TGetStrProc); override;
end;

procedure Register;

implementation

procedure TMySelectionEditor.RequiresUnits(Proc: TGetStrProc);
var
  comp: TMyComponent;
  I: Integer;
begin
  inherited RequiresUnits(Proc);
  Proc('ExtraUnit');  
  // might be a better way of doing the code from here onwards?
  if (Designer=nil)or(Designer.Root=nil) then Exit;

  for I := 0 to Designer.Root.ComponentCount - 1 do
  begin
      if (Designer.Root.Components[i] is TMyComponent) then
      begin
        comp := TMyComponent(Designer.Root.Components[i]);
        if comp.SampleProperty = True then
            Proc('ExtraUnit2');
        Proc(comp.ObjProperty.UnitName);
      end;
  end;
end;

procedure Register;
begin
  RegisterSelectionEditor(TMyComponent, TMySelectionEditor);
end;
Dan Bartlett
There really should be an easier way than this, it's easy to get a TComponent's unit added to the uses clause, but as soon as it's a TPersistent created dynamically at designtime, or a type in an event that doesn't exist in one of the units in the component's class, then it gets a lot harder to add.
Dan Bartlett
When will the Selection Editor be called - only when the developer drops a new component on the form? In this case, SampleProperty would still have the default value, and a change to a different value would have no effect.
mjustin
No, it seems to be called only when you compile (once per component), so it will depend on the value of the property at that time. There doesn't seem to be a way to refer to the component being processed, so this code loops over all the components placed on the form (you might need to modify to allow sub-properties)
Dan Bartlett