Integration with Source Control

Topics: General
Sep 29, 2009 at 4:36 PM

I recently started using VFP IDE integration with a Source Control System (SVN).

I'm using SccTextX + PushOK SVNSCC + TortoiseSVN software to integrate VFP and SVN, but I think my considerations could apply even to CVS or the Microsoft product.
There are some bad things in standard VFP source control integration:

1. whenever I do a re-buid of the project all binary files (VCX/VCT, SCX/STC, FRX/FRT) are touched even if there is no code change, in this way when the new versions are committed to the repository the version number increase without need, and moreover others developers need to re-download new versions.

2. after re-build, VFP mark binary files as checked-out but text equivalents (SCA,VCA,FRA) are still read-only, so when a check-in is done the developer is asked to confirm file by file an overwrite (and this could involve a lot of files!). I solved this modifying SccTextX code and commenting out the read-only overwrite control in the case of text files, but this is a dirt solution!

3.  it seems SccTextX (and the original SccText) are capable to convert text files back to binary files, but the feature is disabled. Maybe someone tried to activate this feature ant tested? In this case would be possible to put only text files in the repository solving problems 1 and 2 and maybe doing even merges.

I saw there is a Subfox project on codeplex but is seems there is no source code or any other file to download.

There are very few informations around about VFP and Source Control. Does anyone have a solutions at least at the points  1 and 2? Any comment would be appreciated.

Sep 29, 2009 at 11:21 PM

Hi,

I am using a similar setup as yourself and have come across the same issues. I found it a pain to undo-checkout of all the files I haven't modified after doing a build. 

I overcame issues 1+2 by creating an application (.Net I know... but I needed case-sensitive filenames) along with a project hook that fires the app on the pre and post build. Long story short, the files that FoxPro likes to inappropriately touch get backed up into a temporary directory. The files' flags are set to Read/Write. FoxPro does it's thing and then the app restores the files back to there original settings. The VFP IDE is none the wiser.

This has come in handy for working on a project which uses shared tools that are controlled by a different project. I was finding I had to check-out the tools just to do a build because they would be read-only.

You are more than welcome to give the app a try and see if it helps alleviate some of the issues?

Shane

 

Sep 30, 2009 at 7:58 AM
Edited Oct 1, 2009 at 7:03 AM

I'm very interested in your solution.

Could you send me the application ?

Oct 1, 2009 at 2:27 PM

I'm trying to use a SVN+TortoiseSVN+TwoFox combination. I'm just at the begining but it looks promising. 
TwoFox is capable of converting vfp meta files to text (xml) and back so it's ideal, but i've had to correct a few shortcommings.
If TowFox will be included in the Subfox project i'll be happy to contribute. As for TSVN it allows hooks so the conversion programs could be executed after checkout and before commit but i have't got a woking setup ready as i'll have to modify TwoFox for this particular purpose.

If anyone is interested in my progress drop a line.

Oct 1, 2009 at 4:17 PM

Today I decided to work seriously on this problem.

I wrote a ProjectHook that seems to do the right job and to solve problems 1 and 2.

My work is based partly on the idea of mozenwrath (make a backup copy of binary files) and partly on the work of Pamela Thalacker in the May 2001 issue of FoxPro Advisor (change R/W attributes with a ProjectHook).

My code is pure VFP code and solve even the problem of case sensitive file names.

I'll do some other tests and then I'll publish the code.

Oct 2, 2009 at 10:40 AM

This is the ProjectHook I wrote and seems to work very well.

Please consider I done many tests but maybe there is some little bugs....

The code is based on a project hook of Pamela Thalacker but addressing another problem (R/W flags), then I used the idea of Shane Charles to backup files before build and restore them after build. Of course only unmodified (checked-in) binary files are restored, but a problem can arise in case an header file is touched. In this case all forms,classes etc... depending on the header files needs to be recompiled even if they are checked-in. If the hook find a checked-out header file (.h) it show an alert telling the user to check-out depending files. Maybe this could be enhanced. 

The code is pure VFP code and addresses the problem of case preserving file names and read-only flags.

 


 

 

 

 

* Author: Andrea Mariottini (andrea.mariottini.mc@gmail.com)

*

* This program is based on code/ideas of Pamela Thalacker and Shane Charles

*

* LICENSE:

* You may use, distribute, and modify the program as long as you adhere to the restrictions listed below.

* - This program is provided AS IS and I'm not liable for any damages resulting from the use or abuse of this program.

* - Don’t misrepresent the origin of this program.

*

**************************************************

*-- Class:        svnprojecthook

*-- ParentClass:  projecthook

*-- BaseClass:    projecthook

*-- Time Stamp:   10/02/09 12:05:07 PM

*

DEFINE CLASS svnprojecthook AS projecthook





	Height = 22

	Width = 23

	PROTECTED my_temp_dir_name = ("__PRE_BUILD_COPY__")

	PROTECTED log_file_name = ("log.txt")

	PROTECTED logfile = ("")

	Name = "svnprojecthook"

	PROTECTED aFiles[1]





	PROCEDURE BeforeBuild

		*

		* This project hook make a backup copy of all checked-in files so they could be restored after build.

		* This means only checked-out files are really recompiled (and touched) at the end of the process.

		*

		* If an header file is checked-out probably even some checked-in files have to be recompiled, so the code alert the user

		* to check-out files depending of the header. If this is not done then the new header is not applied.

		*

		* Files are copied to THIS.my_temp_dir (__PRE_BUILD_COPY__ by default)

		* Files ouside Project Home are not copied.

		*

		LPARAMETERS cOutputName, nBuildAction, lRebuildAll, lShowErrors, lBuildNewGuids

		LOCAL lnNum2Change, lnIdx, llProceed, n, lcMessage



		llProceed = .T.

		llErr = .F.



		IF lRebuildAll



			*-- Case preserving file copy (params: file1, file2, failifexists)

			DECLARE LONG CopyFile IN WIN32API STRING, STRING, LONG

			*-- API funtions to get and set file attributes

			DECLARE INTEGER SetFileAttributes IN kernel32 STRING, INTEGER 

			DECLARE INTEGER GetFileAttributes IN kernel32 STRING



			WITH _VFP.ActiveProject



				*-- clear error log

				THIS.logfile = ADDBS(_VFP.ActiveProject.HomeDir)+ THIS.my_temp_dir_name + "\" + THIS.log_file_name

				STRTOFILE(TTOC(DATETIME())+CHR(13)+CHR(10),THIS.logfile)



				lnNum2Change = 0

				n = .files.count

				FOR lnIdx = 1 TO n



					WAIT "SVNProjectHook" + CHR(13) + "Collecting files... " + ALLTRIM(STR(lnIdx)) + "/" + ALLTRIM(STR(n)) + CHR(13) + "File: " + JUSTFNAME(.Name) WINDOW NOWAIT



					WITH .Files(lnIdx)



						*-- If it is an header file maybe a rebuild of checked-in files is needed

						IF .Type $ 'PT' AND UPPER(RIGHT(.Name, 2)) = '.H' AND INLIST(.SCCStatus,2,6) && checke-out

							IF MESSAGEBOX("The File " + UPPER(JUSTFNAME(.Name)) + " is checked-out so it can have changes and a rebuild of checked-in files could be needed."+CHR(13)+;

										"In this case, please check-out all files depending on this header file before proceed."+CHR(13)+;

										"Proceed?",4+32,"SVNProjectHook") != 6

								llProceed = .F.

								EXIT

							ENDIF

						ENDIF

						*-- We only need to save the relevant file types (classes,reports,lables,menus,forms)

						IF !(.Type $ 'VRBMK')

							LOOP

						ENDIF

						*-- backup only checked-in files 

						IF INLIST(.SCCStatus,2,6)

							*-- checke-out: don't copy

							LOOP

						ENDIF



						lnNum2Change = lnNum2Change + 1

						DIMENSION This.aFiles[lnNum2Change, 2]

						*--Add the memo file. The next line changes, for example, FOO.SCX to FOO.SCT.

						THIS.aFiles[lnNum2Change, 1] = THIS.CaseSensitiveFileName(.Name)

						THIS.aFiles[lnNum2Change, 2] = THIS.BackupPath(THIS.aFiles[lnNum2Change, 1])

						lnNum2Change = lnNum2Change + 1

						DIMENSION This.aFiles[lnNum2Change, 2]

						THIS.aFiles[lnNum2Change, 1] = THIS.CaseSensitiveFileName(LEFT(.Name, LEN(.Name)-1) + 't')

						THIS.aFiles[lnNum2Change, 2] = THIS.BackupPath(THIS.aFiles[lnNum2Change, 1])



					ENDWITH



				ENDFOR



			ENDWITH



			IF llProceed

				WITH THIS

					*-- Copy files to backup dir

					FOR lnIdx = 1 TO lnNum2Change 

						WAIT "SVNProjectHook" + CHR(13) + "Copying files... " + ALLTRIM(STR(lnIdx)) + "/" + ALLTRIM(STR(lnNum2Change)) + CHR(13) + "File: " + JUSTFNAME(.aFiles[lnIdx,1]) WINDOW NOWAIT

						IF !EMPTY(.aFiles[lnIdx,1]) AND !EMPTY(.aFiles[lnIdx,2])

							*-- if destination file exist it is read-only so make sure it isn't

							IF FILE(.aFiles[lnIdx,2])

								SetFileAttributes(.aFiles[lnIdx,2],BITCLEAR(GetFileAttributes(.aFiles[lnIdx,2]),0))

							ENDIF

							IF CopyFile(.aFiles[lnIdx,1],.aFiles[lnIdx,2],.F.) = 0

								lcMessage = "Cannot copy " + .aFiles[lnIdx,1] + " to " + .aFiles[lnIdx,2]

								STRTOFILE(lcMessage+CHR(13)+CHR(10),THIS.logfile,1)

								MESSAGEBOX(lcMessage + CHR(13) + "Aborting compilation.",16,"SVNProjectHook")

								llProceed = .F.

								EXIT

							ENDIF

						ENDIF

					ENDFOR

				ENDWITH

			ENDIF



			IF !llProceed

				*-- calcel project build

				NODEFAULT

				STRTOFILE("Copy to " + THIS.my_temp_dir_name + " aborted" + CHR(13)+CHR(10),THIS.logfile,1)

			ELSE

				STRTOFILE("All files copied to " + THIS.my_temp_dir_name + CHR(13)+CHR(10),THIS.logfile,1)

			ENDIF



			WAIT CLEAR



		ENDIF

	ENDPROC





	PROCEDURE AfterBuild

		*

		* This project hook restore a backup copy of all checked-in files

		*

		LPARAMETERS nError



		LOCAL lnIdx,n



		*-- Case preserving file copy (params: file1, file2, failifexists)

		DECLARE LONG CopyFile IN WIN32API STRING, STRING, LONG

		*-- API funtions to get and set file attributes

		DECLARE INTEGER SetFileAttributes IN kernel32 STRING, INTEGER 

		DECLARE INTEGER GetFileAttributes IN kernel32 STRING



		llErr = .F.



		WITH THIS

			*-- Copy back all files

			n = ALEN(.aFiles,1) 

			FOR lnIdx = 1 TO n

				WAIT "SVNProjectHook" + CHR(13) + "Restoring files... " + ALLTRIM(STR(lnIdx)) + "/" + ALLTRIM(STR(n)) + CHR(13) + "File: " + JUSTFNAME(.aFiles[lnIdx,1]) WINDOW NOWAIT

				IF !EMPTY(.aFiles[lnIdx,1]) AND !EMPTY(.aFiles[lnIdx,2])

					*-- make sure file is not readonly so can overwrite

					SetFileAttributes(.aFiles[lnIdx,1],BITCLEAR(GetFileAttributes(.aFiles[lnIdx,1]),0))

					*-- overwrite file with backup copy

					IF CopyFile(.aFiles[lnIdx,2],.aFiles[lnIdx,1],.F.) = 0

						llErr = .T.

						STRTOFILE("Cannot copy back " + .aFiles[lnIdx,2] + " to " + .aFiles[lnIdx,1] + CHR(13)+CHR(10),THIS.logfile,1)

					ENDIF

				ENDIF

			ENDFOR

		ENDWITH



		WAIT CLEAR



		IF llErr = .T.

			MESSAGEBOX("Errors encountered, click OK to see log file.",16,"SVNProjectHook")

			MODIFY FILE (THIS.logfile) NOEDIT NOWAIT

		ENDIF

	ENDPROC





	PROCEDURE casesensitivefilename

		* Given a file path return the same file path but preserving case

		LPARAMETERS lcFile



		IF ADIR(larray,lcFile,"",1) = 1

			lcCaseSensitivePath = ADDBS(JUSTPATH(lcFile)) + larray[1,1]

		ELSE

			*-- this should never happen

			lcCaseSensitivePath = ""

		ENDIF



		RETURN lcCaseSensitivePath

	ENDPROC





	PROCEDURE backuppath

		* Given a file path in the project return a buckup path

		* files ouside project home are not backed-up

		LPARAMETERS lcFile



		LOCAL lcProjectHome,n,lcPath



		lcProjectHome = ADDBS(_VFP.ActiveProject.HomeDir)

		n = LEN(lcProjectHome)

		IF UPPER(LEFT(lcFile,n)) == UPPER(lcProjectHome)

			*-- file is in project home

			lcPath = lcProjectHome + THIS.my_temp_dir_name + "\" + SUBSTR(lcFile,n+1)

			TRY

				*-- create dir if not exits

				MKDIR(JUSTPATH(lcPath))

			CATCH WHEN .T. 

			ENDTRY



			RETURN  lcPath 

		ELSE

			*-- Files not in project home are not copied

			RETURN ""

		ENDIF

	ENDPROC



ENDDEFINE

*

*-- EndDefine: svnprojecthook

**************************************************

 

Oct 3, 2009 at 9:37 PM

Two bug in my code:

1. I let an "IF lRebuildAll" condition that could be removed

2. In the AfterBuild method add a check if file array is not initialized

 

Two comments:

1. the problem of header files could be solved inspecting binary file for presence of the include directive

2. as I know the only reason to recompile untouched files is because an header file is changed. The entire logic of the code rely on this assumption, maybe this is wrong? Could be other cases?