Epigraph: "Let this inspire you to feat!" (Bel Kaufman, Up the Downstairs).
About crutches and bicycles, an integral part of modern necromancy.
This is the story of integrating one single solution into the development process. The solution has been brought to the final result, the link to the repository will be further in the text.
It would seem, what could be simpler and more natural than transparent, without "ridiculous body movements", work with resources placed in a specially allocated folder for this? What if the development environment is released at the millennium border?
, , «» Delphi 7, SQL . ( , -), , — « ». , … , SSMS, . , Delphi, SQL , .
«» : ! ( ) , . , , *.rc , .
, - IDE Delphi 7 - . , . MS Build, - ! , , , - IDE .
, (*.rc) , . , , ( ), . rc- ! , .
. , , . , «-» , ( - ). , ( SQL) . , , ( ), … , - , .
.dfm — « ». , , … , ? ( ). .
object SqlSALES: TSqlVar
SQL.Strings = (
'SET NOCOUNT ON'
''
'IF OBJECT_ID(N'#39'tempdb..#Sales'#39', N'#39'U'#39') IS NOT NULL '
'DROP TABLE #Sales;'
''
'IF OBJECT_ID(N'#39'tempdb..#ClientIDs'#39', N'#39'U'#39') IS NOT NULL '
'DROP TABLE #ClientIDs;'
''
'-- '#1055#1072#1088#1072#1084#1077#1090#1088#1099': '#1076#1072#1090#1072' '#1086#1090', '#1076#1072#1090#1072' '#1076#1086', '#1075#1088#1091#1087#1087#1072
'-- '#1056#1077#1072#1083#1080#1079#1072#1094#1080#1103
'SELECT '
#9'OperDate, -- '#1044 +
#1072#1090#1072
...
, : , IDE, . , , . , , Delphi, , - . — ...
.
, - , - .
, . :
!!! , ( build)
.
— . « ».
Gulp.js — , . . . .
. -, , , .rc . Delphi. -, IDE, , (-, , , IDE ). — IDE .
( ) , USES, :
{%File 'Res\SRC\SQL\Import\Sectors\leafSectors.sql'}
, .
, . :
SomeProject.dpr
library SomeProject;
{
!
.
res/CompileRc/CompileAllResources.cmd
_ .cmd
, res\src
res/CompileRc/CompileAllResources.cmd
,
'Res\AutoGenerated.rc'. <=
- -----^^^^^^^^^^^^^^^^^^^^ Ctrl + Enter
}
{<AUTOGENERATED_RC>}
{%File 'Res\SRC\SQL\DB_Updates.sql'}
{%File 'Res\SRC\SQL\Foo\Sales.sql'}
{%File 'Res\SRC\SQL\Foo\Stocks.sql'}
...
{$R 'Res\AutoGenerated.res' 'Res\AutoGenerated.rc'}
{</AUTOGENERATED_RC>}
uses
...
AutoGenerated.rc ():
SQL_DB_Updates RCDATA PREPARED\SQL_DB_Updates
SQL_Foo_Sales RCDATA PREPARED\SQL_Foo_Sales
SQL_Foo_Stocks RCDATA PREPARED\SQL_Foo_Stocks
...
:
gulpfile.js
const { watch, series } = require('gulp');
const spawn = require('child_process').spawn;
function compileResources(cb) {
var cmd = spawn('CompileRc\\CompileAllResources.cmd', [], {stdio: 'inherit'});
cmd.on('close', function (code) {
cb(code);
});
}
exports.default = function() {
compileResources(code=>{})
watch('Src/**', compileResources); // series(compileResources, ...)
};
, , ( ). .
, , . :
>gulp --version
CLI version: 2.3.0
Local version: 4.0.2
>node -v
v12.20.0
, — .
:
,
Perl, , , rc-, ()
dpr . ,
rc-
( .
.
CompileAllResources.cmd
@Echo off
set BatchDir=%~dp0
cd %BatchDir%
touch ..\AutoGenerated.rc
FOR %%i IN ("%BatchDir%..") DO (set target=%%~fi)
echo.
echo [%TIME%] STARTING: ===== %target% =====
echo.
if not exist %BatchDir%..\prepared\*.* md %BatchDir%..\prepared > nul
call %BatchDir%..\AutoCompileRc.Config.cmd
FOR %%i IN ("%DprFile%") DO (set DprFileOnly=%%~nxi)
call %BatchDir%WaitWhileRunned.cmd git
cd %BatchDir%..
%BatchDir%bin\find SRC -type f | %BatchDir%bin\grep -E -v --file=%BatchDir%excludes.lst>%BatchDir%ResFiles.lst
%BatchDir%bin\perl %BatchDir%CreateRc.pl %BatchDir%ResFiles.lst AutoGenerated.rc %BatchDir%RcSources.lst %BatchDir%PrepareIt.cmd
cd %BatchDir%
echo {$R 'Res\AutoGenerated.res' 'Res\AutoGenerated.rc'}>>RcSources.lst
call between.cmd %DprFile% "\{<AUTOGENERATED_RC>\}\s*" "\{<\/AUTOGENERATED_RC>\}">RcSourcesOld.lst
fc RcSources.lst RcSourcesOld.lst>nul
if errorlevel 1 (
call ReplaceBetween.cmd %DprFile% RcSources.lst "\{\<AUTOGENERATED_RC\>\}\s*" "\{\<\/AUTOGENERATED_RC\>\}">%DprFileOnly%.tmp
copy %DprFileOnly%.tmp %DprFile%>nul
del %DprFileOnly%.tmp
)
echo Preparing updated and new files...
cd ..
call %BatchDir%PrepareIt.cmd
echo Compiling resources...
brcc32 AutoGenerated.rc
cd %BatchDir%
echo.
echo [%TIME%] DONE: ===== %target% =====
echo.
, find grep ( git) . , . , . ( « ») :
CreateRc.pl
#!c:/Perl/bin/perl
open (IN, "<".$ARGV[0]) || die $!; #
open (RC_ENC, ">".$ARGV[1]) || die $!; # (.rc)
open (LST, ">".$ARGV[2]) || die $!; #
# ,
# {<AUTOGENERATED_RC>} {</AUTOGENERATED_RC>}
open (ENC_CMD, ">".$ARGV[3]) || die $!; # (.cmd)
# (, )
while(<IN>){
split /\n/;
$File = $_; #
$File =~ s/\//\\/g; # -
$File =~ s/\n//; # -
$Name = $File; #
$Name =~ s/^Src\\//i; # - ()
$Name =~ s/\\/_/g; # - —
$Name =~ s/\..*$//; # -
$Dest = "PREPARED\\".$Name."";
$_ = $File;
$EncryptIt = ! /\\Bin\\/i; # bin
print RC_ENC $Name
, substr(" ", 1, 40 - length($Name))
, " RCDATA "
, $Dest
, "\r\n";
$_ = $File;
if (! /\\Bin\\/i) {
# (: dpr )
# bin
print LST
"{\%File 'Res\\"
, $File
, "'}\r\n";
}
# :
# ( )
if (! (-f $Dest) || ( (stat $File)[9] > (stat $Dest)[9] ) ) {
if ($EncryptIt) {
# : encrypt.cmd <> <> < >
# .
# ncrypt.cmd .
print ENC_CMD "call encrypt.cmd "
, $File
, substr(" ", 1, 40 - length($File))
, " "
, $Dest
, " \""
, lc $Name
, "\"\r\n";
} else {
print ENC_CMD "copy "
, $File
, substr(" ", 1, 40 - length($File))
, " "
, $Dest
, ">nul\r\n";
}
}
}
close IN;
close RC_ENC;
close LST;
close ENC_CMD;
. , git ( ) - . , grep , . Find - ( , , , , ). — , , , , . - — , .
, , — , IDE.
, , - . ( , ) , ( node.js), ?
— FolderMonitor, Delphi (https://bitbucket.org/danik-ik/foldermonitor/src/master/). , (, , , . https://bitbucket.org/danik-ik/compilerc/src/master/README.md).
CompileRc git. . (), « ». SmartGit, CompileRc ( CompileRc, , ), ( ):
, . , , , — . , (// ).
:
(******************************************************************************
( ) ,
.
:
https://webdelphi.ru/2011/08/monitoring-izmenenij-v-direktoriyax-i-fajlax-sredstvami-delphi-chast-1/
:
-
-
-
******************************************************************************)
unit FolderMonitorCore;
interface
uses Classes, Windows, SysUtils;
type
TFolderMonitorCore = class(TThread)
private
FDirectory: string;
FScanSubDirs: boolean;
FOnChange : TNotifyEvent;
procedure DoChange;
public
constructor Create(ASuspended: boolean; ADirectory:string; AScanSubDirs: boolean);
property OnChange: TNotifyEvent read FOnChange write FOnChange;
protected
procedure Execute; override;
end;
implementation
{ TFolderMonitorCore }
constructor TFolderMonitorCore.Create(ASuspended: boolean; ADirectory: string;
AScanSubDirs: boolean);
begin
inherited Create(ASuspended);
FDirectory:=ADirectory;
FScanSubDirs:=AScanSubDirs;
FreeOnTerminate:=true;
end;
procedure TFolderMonitorCore.DoChange;
begin
if Assigned(FOnChange) then
FOnChange(Self);
end;
procedure TFolderMonitorCore.Execute;
var ChangeHandle: THandle;
begin
// ,
ChangeHandle:=FindFirstChangeNotification(PChar(FDirectory),
FScanSubDirs,
FILE_NOTIFY_CHANGE_FILE_NAME+
FILE_NOTIFY_CHANGE_DIR_NAME+
FILE_NOTIFY_CHANGE_SIZE+
FILE_NOTIFY_CHANGE_LAST_WRITE
);
// ,
{$WARNINGS OFF}
Win32Check(ChangeHandle <> INVALID_HANDLE_VALUE);
{$WARNINGS ON}
try
//
while not Terminated do
begin
{ : ,
}
case WaitForSingleObject(ChangeHandle, 1000) of
WAIT_FAILED: Terminate; {, }
WAIT_OBJECT_0: //
begin
// —
//
// ( Sublime Text ).
// :
// , .
// .
sleep(5);
WaitForSingleObject(ChangeHandle, 1); // ,
// :
if not Terminated then
Synchronize(DoChange);
end;
WAIT_TIMEOUT: {DO NOTHING}; // ,
//
end;
FindNextChangeNotification(ChangeHandle);
end;
finally
FindCloseChangeNotification(ChangeHandle);
end;
end;
end.
. . , . (, VS Code). , , ( , Delphi, rc-). Delphi AutoGenerated.rc. Delphi, rc- , . . , , : ( ) → → Delphi → Reload? Yes! → . .
, «» , - . , , ( ), - . , .
— , , .
P.S.
, , , : « », .
UPD:
I was asked in PM about the compiled FolderMonitor. If you have nothing to compile with, you can take it here: https://disk.yandex.ru/d/VTbuGvB5jabD6w
Perhaps it was left behind the scenes: this solution only generates code valid for Delphi and automates the routine : it timely adds a resource file to the compilation list and to the list of external project files, and timely compiles rc to RES (using Delphi means). Everything. Encrypts in a specific project, but this is just an overkill in the general case. By running it under the change monitor, I move the work with resources from the static state ("created once and for all") to the dynamic ("editing on the fly").