Warn if multiple same procedures files are found?

Over years I had rare, but annoying issue with users who accidentally had same named procedure files in their User Procedures search path. Always by accident - old files, old links to multiple user files locations,... It is my understanding, that Igor loads the first ipf file with correct name found - and that can cause problems if that is some obsolete file. But users are generally not aware that they have this problem and I get reports with bugs fixed years ago even after they installed new versions of packages. 

And yes, my installler tries to delete all copies of obsolete files before copying new one, but some may be read only or available only randomly, so it does not work always. 

Should Igor throw error when multiple files with the same names are found in the search path for user procedure files? At least provide some warning? I believe that this situation should be always considered as major problem. 

 

I can imagine one approach that you could take independently. Hard-code a static k_version constant in each procedure file. Have a function that assembles those constants. Check them against the preferred (most recent) values. Give a warning for values that have "expired". You could have this function as part of your update package. Or you could provide this option within a stand-alone package-certifier procedure.

That is what Igor has already : #pragma version =  

I already use the #pragma version for each ipf file and my loader function requires appropriate minimum version for each file. Igor will complain if it finds old library version.  

This works if :

1. I do not forget to promote properly versions for all files (50+ files to deal with, each different version...)

2. The loader file used is the current one and not the one from old distribution. 

And it still does not explain to user why they have a problem, just tells them they need newer version of a file (while they probably think they have the latest version).  This is rare issue so this is not major problem to address. But may be something to think about in the future. I think Igor does a lot more complicated things on start up and this sounds like simple check to help users figure out problems.  

I am not sure I understand how this can happen (is the physical file and a link to a similar file elsewhere in the same folder or something?), but how about checking the path of key functions in your procedure file(s) via FunctionPath(). If the procedure file is not in the folder specified by your installer (e.g., \Documents\WaveMetrics\Igor Pro XX User Files\User Procedures), tell the user that the file in folder XXX or a link to this folder is obsolete/not-to-spec and must be deleted. Or you can check if at least all procedure files are in the same folder and give a waring should there be outliers.

For those thinking about this problem, here is an example of how to reproduce the problem. I tested this only on Windows with IP9.

First, create a procedure file that contains only the following function:

Function fooFunc()
    print 1
End

 

Save this file as Foo.ipf in your Igor Pro 9 User Files/User Procedures directory. Then quit Igor.

Using Windows Explorer, navigate to the Igor Pro 9 User Files/User Procedures directory. Create a new directory within that directory named testFolder. Make a copy of the Foo.ipf file and place the copy in testFolder. With a text editor, edit the copy of Foo.ipf within testFolder and change it to print 2.

At this point you have almost identical copies of one procedure file directly within User Procedures and within a subdirectory which itself is in User Procedures.

Start Igor 9. In the main procedure window, add this line:

#include "foo"

Then on the command line, execute:

•foofunc()
  1

In this particular test, the version of Foo.ipf that is directly within User Procedures is the one that is used.

Now quit Igor.

Using Windows Explorer, go back to the Igor Pro 9 User Files/User Procedures directory. Create a new directory named zestFolder. Move the copy of Foo.ipf that is currently in User Procedures into zestFoolder.

Now you have two copies of Foo.ipf, each within its own subdirectory within User Procedures.

Start Igor and add the #include statement from earlier. Then run the test function:

•foofunc()
  2

The fact that foofunc() prints a different value in the two situations indicates that a different instance of Foo.ipf is included. This is the problem that Jan wishes to avoid.

Now, to address what is actually happening.

There are 4 different forms of the include statement. You can read about these by executing:

DisplayHelpTopic "The Include Statement"

The type used in my example above and mentioned by Jan is form 2, described as "Igor searches for the named file in "Igor Pro Folder/User Procedures" and "Igor Pro User Files/User Procedures" and in any subfolders:"

While I don't believe that any of the implementation details of this search are documented, the way it currently works is that we generate a list of procedure file names that are within either of the User Procedures folders. This list is generated only once (the first time it is needed). Then to satisfy the include, the file name of each file in the list is compared to the target file name, and upon the first match that file is used to satisfy the include statement. So in cases like this where the same file name exists in two different places, the one that is found first will be used.

The order in which subdirectories are searched will be the order they are given to Igor by the underlying OS. On Windows I think this is always alphabetical order. On macOS, I think this is the order in which the directory was created. So the example I gave in my last post may work differently on macOS than on Windows.

I think you might want to consider using form 4 of the include statement. That would limit the search to a subdirectory within the two User Procedures directories, though it wouldn't prevent a conflict if *both* of the User Procedures directories contained a directory with the same name but different contents (for example, different versions of your package).

Since it is perfectly legal and valid for two procedure files of the same name to be found within User Procedures (e.g. package1 and package2 directories both contain a file named constants.ipf) I don't think adding the suggested warning would be appropriate. It might help in some situations but it might confuse the user that is not doing anything wrong in other situations.

I don't think there is a great solution to this problem. For what it's worth, we sometimes run into the same problem ourselves when compiling Igor. We must be careful to not have header files with the same file name in different directories for pretty much the same reason.

That was great explanation Adam,

I do not think there needs to be warning every time there are two or more same named ipf files, only when Igor finds that method used to identify the file which should be included is not unique. If we use method 2 we would find out in your example that we do not have unique solution for foo.ipf - there are two foo.ipf files which both satisfy the method 2 in your example. However, if Igor is directed using method 4 : #include "testFolder:foo" there is only one solution to this inclusion and no warning should be needed, that is completely legal situation. 

I understand there are issues this may cause which I am not aware of... It might be useful to at least print message to history about this being problem if not raise more significant warning. I see that you are well aware of this problem, so whatever you choose to do is fine. 

I think one can detect the case of multiple procedure files named the same via a combination of AfterCompiledHook/WinList/FindDuplicates.

... > I see that you are well aware of this problem, so whatever you choose to do is fine.

In the meantime, would it not be useful to create your own method to check across the procedure files themselves, preferably without the need to open the procedures in Igor Pro? The approach could use a shell script or an AppleScript on macOS and a batch script on Windows. Alternatively, it could be a stand-alone Igor Pro procedure that does nothing more than perform a sanity check on the files that are supposed to be in the user's current installation.

I extracted code from the updater project that can be used to check for duplicates - attached here. It seems to work, but I haven't really tested much. Perhaps a starting point for running a check for problematic duplicates.

The function CheckProcVersions() checks for duplicates of currently included user procedure files.